diff options
| author | bors <bors@rust-lang.org> | 2024-12-03 09:47:39 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-12-03 09:47:39 +0000 |
| commit | ae3703cdf271b07cbc7cbaeda0ea8ab6bba60160 (patch) | |
| tree | 877a715e925340171b87e61918a7cbcf1c7770c6 /compiler | |
| parent | efdd9e802053caeb52103945df858e87f837e59a (diff) | |
| parent | 8ae11145cefdd3cf3fe693ca871a29c91050ab82 (diff) | |
| download | rust-ae3703cdf271b07cbc7cbaeda0ea8ab6bba60160.tar.gz rust-ae3703cdf271b07cbc7cbaeda0ea8ab6bba60160.zip | |
Auto merge of #133788 - matthiaskrgr:rollup-1p100a8, r=matthiaskrgr
Rollup of 6 pull requests
Successful merges:
- #132723 (Unify `sysroot_target_{bin,lib}dir` handling)
- #133041 (Print name of env var in `--print=deployment-target`)
- #133325 (Reimplement `~const` trait specialization)
- #133395 (Add simd_relaxed_fma intrinsic)
- #133517 (Deeply normalize when computing implied outlives bounds)
- #133785 (Add const evaluation error UI test.)
r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
18 files changed, 173 insertions, 68 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index f787b8a6fd9..e0ebe30752a 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -415,7 +415,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }); } - sym::simd_fma => { + // FIXME: simd_relaxed_fma doesn't relax to non-fused multiply-add + sym::simd_fma | sym::simd_relaxed_fma => { intrinsic_args!(fx, args => (a, b, c); intrinsic); if !a.layout().ty.is_simd() { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 604678a9af4..79d1a06dd46 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -772,6 +772,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( sym::simd_flog => "log", sym::simd_floor => "floor", sym::simd_fma => "fma", + sym::simd_relaxed_fma => "fma", // FIXME: this should relax to non-fused multiply-add when necessary sym::simd_fpowi => "__builtin_powi", sym::simd_fpow => "pow", sym::simd_fsin => "sin", diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index da7f94e8cf7..d8b055137b3 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1534,6 +1534,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)), sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)), sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)), + sym::simd_relaxed_fma => ("fmuladd", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)), sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)), sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)), sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)), @@ -1572,6 +1573,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( | sym::simd_fpowi | sym::simd_fsin | sym::simd_fsqrt + | sym::simd_relaxed_fma | sym::simd_round | sym::simd_trunc ) { diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index 93d90cd16b2..d9c5c3e5af9 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -97,7 +97,7 @@ fn minimum_deployment_target(target: &Target) -> OSVersion { } /// Name of the environment variable used to fetch the deployment target on the given OS. -fn deployment_target_env_var(os: &str) -> &'static str { +pub fn deployment_target_env_var(os: &str) -> &'static str { match os { "macos" => "MACOSX_DEPLOYMENT_TARGET", "ios" => "IPHONEOS_DEPLOYMENT_TARGET", diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 34a2464972a..78d69a66edc 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -415,6 +415,10 @@ impl<O: ForestObligation> ObligationForest<O> { .collect() } + pub fn has_pending_obligations(&self) -> bool { + self.nodes.iter().any(|node| node.state.get() == NodeState::Pending) + } + fn insert_into_error_cache(&mut self, index: usize) { let node = &self.nodes[index]; self.error_cache diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 85eaae8a104..5b472bb9b81 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -867,8 +867,9 @@ fn print_crate_info( DeploymentTarget => { if sess.target.is_like_osx { println_info!( - "deployment_target={}", - apple::pretty_version(apple::deployment_target(sess)) + "{}={}", + apple::deployment_target_env_var(&sess.target.os), + apple::pretty_version(apple::deployment_target(sess)), ) } else { #[allow(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index cb658111392..070d63b48b7 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -113,8 +113,6 @@ hir_analysis_const_param_ty_impl_on_unsized = the trait `ConstParamTy` may not be implemented for this type .label = type is not `Sized` -hir_analysis_const_specialize = cannot specialize on const impl with non-const impl - hir_analysis_copy_impl_on_non_adt = the trait `Copy` cannot be implemented for this type .label = type is not a structure or enumeration diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3e33120901f..7434bbf180b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -641,7 +641,9 @@ pub fn check_intrinsic_type( | sym::simd_round | sym::simd_trunc => (1, 0, vec![param(0)], param(0)), sym::simd_fpowi => (1, 0, vec![param(0), tcx.types.i32], param(0)), - sym::simd_fma => (1, 0, vec![param(0), param(0), param(0)], param(0)), + sym::simd_fma | sym::simd_relaxed_fma => { + (1, 0, vec![param(0), param(0), param(0)], param(0)) + } sym::simd_gather => (3, 0, vec![param(0), param(1), param(2)], param(0)), sym::simd_masked_load => (3, 0, vec![param(0), param(1), param(2)], param(2)), sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit), diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index ed3ae1e12b9..2b33da3c49a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -117,13 +117,12 @@ where } f(&mut wfcx)?; - let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?; - let errors = wfcx.select_all_or_error(); if !errors.is_empty() { return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } + let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?; debug!(?assumed_wf_types); let infcx_compat = infcx.fork(); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 51115b11e86..4142dcff226 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1080,13 +1080,6 @@ pub(crate) struct EmptySpecialization { } #[derive(Diagnostic)] -#[diag(hir_analysis_const_specialize)] -pub(crate) struct ConstSpecialize { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(hir_analysis_static_specialize)] pub(crate) struct StaticSpecialize { #[primary_span] diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 246643d8074..ee55e1bc21a 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -66,7 +66,6 @@ //! on traits with methods can. use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::outlives::env::OutlivesEnvironment; @@ -134,7 +133,6 @@ fn check_always_applicable( unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args) }; - res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span)); res = res.and(check_static_lifetimes(tcx, &parent_args, span)); res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span)); res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span)); @@ -157,30 +155,6 @@ fn check_has_items( Ok(()) } -/// Check that the specializing impl `impl1` is at least as const as the base -/// impl `impl2` -fn check_constness( - tcx: TyCtxt<'_>, - impl1_def_id: LocalDefId, - impl2_node: Node, - span: Span, -) -> Result<(), ErrorGuaranteed> { - if impl2_node.is_from_trait() { - // This isn't a specialization - return Ok(()); - } - - let impl1_constness = tcx.constness(impl1_def_id.to_def_id()); - let impl2_constness = tcx.constness(impl2_node.def_id()); - - if let hir::Constness::Const = impl2_constness { - if let hir::Constness::NotConst = impl1_constness { - return Err(tcx.dcx().emit_err(errors::ConstSpecialize { span })); - } - } - Ok(()) -} - /// Given a specializing impl `impl1`, and the base impl `impl2`, returns two /// generic parameters `(S1, S2)` that equate their trait references. /// The returned types are expressed in terms of the generics of `impl1`. diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index ba1516655b0..51282b900ed 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -84,6 +84,8 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { self.collect_remaining_errors(infcx) } + fn has_pending_obligations(&self) -> bool; + fn pending_obligations(&self) -> PredicateObligations<'tcx>; /// Among all pending obligations, collect those are stalled on a inference variable which has diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e94c0a5ea6e..5fa8ce835ed 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1844,6 +1844,7 @@ symbols! { simd_reduce_mul_unordered, simd_reduce_or, simd_reduce_xor, + simd_relaxed_fma, simd_rem, simd_round, simd_saturating_add, diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 0f90c45d032..2b2623a050e 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -199,6 +199,10 @@ where errors } + fn has_pending_obligations(&self) -> bool { + !self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty() + } + fn pending_obligations(&self) -> PredicateObligations<'tcx> { self.obligations.clone_pending() } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 03e483f555d..7529ee128f5 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -213,6 +213,10 @@ where } } + fn has_pending_obligations(&self) -> bool { + self.predicates.has_pending_obligations() + } + fn pending_obligations(&self) -> PredicateObligations<'tcx> { self.predicates.map_pending_obligations(|o| o.obligation.clone()) } diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index e99c5eacbd8..ad62b456ad4 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -7,6 +7,7 @@ use rustc_infer::traits::{ FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine, }; use rustc_macros::extension; +use rustc_middle::span_bug; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, @@ -63,10 +64,18 @@ impl<'tcx> At<'_, 'tcx> { if self.infcx.next_trait_solver() { crate::solve::deeply_normalize(self, value) } else { + if fulfill_cx.has_pending_obligations() { + let pending_obligations = fulfill_cx.pending_obligations(); + span_bug!( + pending_obligations[0].cause.span, + "deeply_normalize should not be called with pending obligations: \ + {pending_obligations:#?}" + ); + } let value = self .normalize(value) .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); - let errors = fulfill_cx.select_where_possible(self.infcx); + let errors = fulfill_cx.select_all_or_error(self.infcx); let value = self.infcx.resolve_vars_if_possible(value); if errors.is_empty() { Ok(value) } else { Err(errors) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index c6e41e57f0c..fe47e837dfb 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -59,12 +59,16 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> { - let normalize_op = |ty| { - let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty); + let normalize_op = |ty| -> Result<_, NoSolution> { + // We must normalize the type so we can compute the right outlives components. + // for example, if we have some constrained param type like `T: Trait<Out = U>`, + // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. + let ty = ocx + .deeply_normalize(&ObligationCause::dummy(), param_env, ty) + .map_err(|_| NoSolution)?; if !ocx.select_all_or_error().is_empty() { return Err(NoSolution); } - let ty = ocx.infcx.resolve_vars_if_possible(ty); let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty); Ok(ty) }; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index a9cd705465e..1430cfae51f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::traits::Obligation; use rustc_middle::bug; use rustc_middle::query::LocalCrate; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -224,21 +225,30 @@ pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool tcx.features().specialization() || tcx.features().min_specialization() } -/// Is `impl1` a specialization of `impl2`? +/// Is `specializing_impl_def_id` a specialization of `parent_impl_def_id`? /// -/// Specialization is determined by the sets of types to which the impls apply; -/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies -/// to. +/// For every type that could apply to `specializing_impl_def_id`, we prove that +/// the `parent_impl_def_id` also applies (i.e. it has a valid impl header and +/// its where-clauses hold). +/// +/// For the purposes of const traits, we also check that the specializing +/// impl is not more restrictive than the parent impl. That is, if the +/// `parent_impl_def_id` is a const impl (conditionally based off of some `~const` +/// bounds), then `specializing_impl_def_id` must also be const for the same +/// set of types. #[instrument(skip(tcx), level = "debug")] -pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { +pub(super) fn specializes( + tcx: TyCtxt<'_>, + (specializing_impl_def_id, parent_impl_def_id): (DefId, DefId), +) -> bool { // We check that the specializing impl comes from a crate that has specialization enabled, // or if the specializing impl is marked with `allow_internal_unstable`. // // We don't really care if the specialized impl (the parent) is in a crate that has // specialization enabled, since it's not being specialized, and it's already been checked // for coherence. - if !tcx.specialization_enabled_in(impl1_def_id.krate) { - let span = tcx.def_span(impl1_def_id); + if !tcx.specialization_enabled_in(specializing_impl_def_id.krate) { + let span = tcx.def_span(specializing_impl_def_id); if !span.allows_unstable(sym::specialization) && !span.allows_unstable(sym::min_specialization) { @@ -246,7 +256,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, } } - let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap(); + let specializing_impl_trait_header = tcx.impl_trait_header(specializing_impl_def_id).unwrap(); // We determine whether there's a subset relationship by: // @@ -261,27 +271,123 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, // See RFC 1210 for more details and justification. // Currently we do not allow e.g., a negative impl to specialize a positive one - if impl1_trait_header.polarity != tcx.impl_polarity(impl2_def_id) { + if specializing_impl_trait_header.polarity != tcx.impl_polarity(parent_impl_def_id) { return false; } - // create a parameter environment corresponding to an identity instantiation of impl1, - // i.e. the most generic instantiation of impl1. - let param_env = tcx.param_env(impl1_def_id); + // create a parameter environment corresponding to an identity instantiation of the specializing impl, + // i.e. the most generic instantiation of the specializing impl. + let param_env = tcx.param_env(specializing_impl_def_id); - // Create an infcx, taking the predicates of impl1 as assumptions: + // Create an infcx, taking the predicates of the specializing impl as assumptions: let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - // Attempt to prove that impl2 applies, given all of the above. - fulfill_implication( - &infcx, + let specializing_impl_trait_ref = + specializing_impl_trait_header.trait_ref.instantiate_identity(); + let cause = &ObligationCause::dummy(); + debug!( + "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", + param_env, specializing_impl_trait_ref, parent_impl_def_id + ); + + // Attempt to prove that the parent impl applies, given all of the above. + + let ocx = ObligationCtxt::new(&infcx); + let specializing_impl_trait_ref = ocx.normalize(cause, param_env, specializing_impl_trait_ref); + + if !ocx.select_all_or_error().is_empty() { + infcx.dcx().span_delayed_bug( + infcx.tcx.def_span(specializing_impl_def_id), + format!("failed to fully normalize {specializing_impl_trait_ref}"), + ); + return false; + } + + let parent_args = infcx.fresh_args_for_item(DUMMY_SP, parent_impl_def_id); + let parent_impl_trait_ref = ocx.normalize( + cause, param_env, - impl1_trait_header.trait_ref.instantiate_identity(), - impl1_def_id, - impl2_def_id, - &ObligationCause::dummy(), - ) - .is_ok() + infcx + .tcx + .impl_trait_ref(parent_impl_def_id) + .expect("expected source impl to be a trait impl") + .instantiate(infcx.tcx, parent_args), + ); + + // do the impls unify? If not, no specialization. + let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref) + else { + return false; + }; + + // Now check that the source trait ref satisfies all the where clauses of the target impl. + // This is not just for correctness; we also need this to constrain any params that may + // only be referenced via projection predicates. + let predicates = ocx.normalize( + cause, + param_env, + infcx.tcx.predicates_of(parent_impl_def_id).instantiate(infcx.tcx, parent_args), + ); + let obligations = predicates_for_generics(|_, _| cause.clone(), param_env, predicates); + ocx.register_obligations(obligations); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + specializing_impl_trait_ref, + parent_impl_trait_ref, + errors, + param_env.caller_bounds() + ); + return false; + } + + // If the parent impl is const, then the specializing impl must be const, + // and it must not be *more restrictive* than the parent impl (that is, + // it cannot be const in fewer cases than the parent impl). + if tcx.is_conditionally_const(parent_impl_def_id) { + if !tcx.is_conditionally_const(specializing_impl_def_id) { + return false; + } + + let const_conditions = ocx.normalize( + cause, + param_env, + infcx.tcx.const_conditions(parent_impl_def_id).instantiate(infcx.tcx, parent_args), + ); + ocx.register_obligations(const_conditions.into_iter().map(|(trait_ref, _)| { + Obligation::new( + infcx.tcx, + cause.clone(), + param_env, + trait_ref.to_host_effect_clause(infcx.tcx, ty::BoundConstness::Maybe), + ) + })); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + specializing_impl_trait_ref, + parent_impl_trait_ref, + errors, + param_env.caller_bounds() + ); + return false; + } + } + + debug!( + "fulfill_implication: an impl for {:?} specializes {:?}", + specializing_impl_trait_ref, parent_impl_trait_ref + ); + + true } /// Query provider for `specialization_graph_of`. |
