diff options
| author | Ralf Jung <post@ralfj.de> | 2024-11-28 08:05:16 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-28 08:05:16 +0000 |
| commit | 4624dadc209f595fffc87ef54a54156a5d31cec7 (patch) | |
| tree | dd441ff27ed00a8826fbb8fd042eef1a2293b260 /compiler/rustc_trait_selection/src | |
| parent | 987d642de5bf3c8e263c477ab728db5cd88c0ec3 (diff) | |
| parent | 8e2d72bac8e29cda5b1457720dcdc66def70d9d4 (diff) | |
Merge pull request #4063 from rust-lang/rustup-2024-11-28
Automatic Rustup
Diffstat (limited to 'compiler/rustc_trait_selection/src')
19 files changed, 263 insertions, 320 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 929fa559d75..0f9d4cb1982 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -152,7 +152,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err: TypeError<'tcx>, ) -> Diag<'a> { self.report_and_explain_type_error( - TypeTrace::types(cause, true, expected, actual), + TypeTrace::types(cause, expected, actual), param_env, err, ) @@ -167,7 +167,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err: TypeError<'tcx>, ) -> Diag<'a> { self.report_and_explain_type_error( - TypeTrace::consts(cause, true, expected, actual), + TypeTrace::consts(cause, expected, actual), param_env, err, ) @@ -1792,12 +1792,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn suggest_specify_actual_length( &self, - terr: TypeError<'_>, - trace: &TypeTrace<'_>, + terr: TypeError<'tcx>, + trace: &TypeTrace<'tcx>, span: Span, ) -> Option<TypeErrorAdditionalDiags> { let hir = self.tcx.hir(); - let TypeError::FixedArraySize(sz) = terr else { + let TypeError::ArraySize(sz) = terr else { return None; }; let tykind = match self.tcx.hir_node_by_def_id(trace.cause.body_id) { @@ -1838,9 +1838,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(tykind) = tykind && let hir::TyKind::Array(_, length) = tykind && let hir::ArrayLen::Body(ct) = length + && let Some((scalar, ty)) = sz.found.try_to_scalar() + && ty == self.tcx.types.usize { let span = ct.span(); - Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found }) + Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { + span, + length: scalar.to_target_usize(&self.tcx).unwrap(), + }) } else { None } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 4e7d7b79ff4..75191894525 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -725,7 +725,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &obligation.cause, None, None, - TypeError::Sorts(ty::error::ExpectedFound::new(true, expected_ty, ct_ty)), + TypeError::Sorts(ty::error::ExpectedFound::new(expected_ty, ct_ty)), false, ); diag @@ -1449,7 +1449,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { secondary_span, values.map(|(_, normalized_ty, expected_ty)| { obligation.param_env.and(infer::ValuePairs::Terms(ExpectedFound::new( - true, expected_ty, normalized_ty, ))) @@ -1804,24 +1803,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::highlighted("cargo tree".to_string()), StringPart::normal("` to explore your dependency tree".to_string()), ]); - - // FIXME: this is a giant hack for the benefit of this specific diagnostic. Because - // we're so nested in method calls before the error gets emitted, bubbling a single bit - // flag informing the top level caller to stop adding extra detail to the diagnostic, - // would actually be harder to follow. So we do something naughty here: we consume the - // diagnostic, emit it and leave in its place a "delayed bug" that will continue being - // modified but won't actually be printed to end users. This *is not ideal*, but allows - // us to reduce the verbosity of an error that is already quite verbose and increase its - // specificity. Below we modify the main message as well, in a way that *could* break if - // the implementation of Diagnostics change significantly, but that would be caught with - // a make test failure when this diagnostic is tested. - err.primary_message(format!( - "{} because the trait comes from a different crate version", - err.messages[0].0.as_str().unwrap(), - )); - let diag = err.clone(); - err.downgrade_to_delayed_bug(); - self.tcx.dcx().emit_diagnostic(diag); return true; } @@ -2755,7 +2736,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (obligation.cause.clone(), terr) }; self.report_and_explain_type_error( - TypeTrace::trait_refs(&cause, true, expected_trait_ref, found_trait_ref), + TypeTrace::trait_refs(&cause, expected_trait_ref, found_trait_ref), obligation.param_env, terr, ) @@ -2846,7 +2827,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if Some(expected_trait_ref.def_id) != self.tcx.lang_items().coroutine_trait() && not_tupled { return Ok(self.report_and_explain_type_error( - TypeTrace::trait_refs(&obligation.cause, true, expected_trait_ref, found_trait_ref), + TypeTrace::trait_refs(&obligation.cause, expected_trait_ref, found_trait_ref), obligation.param_env, ty::error::TypeError::Mismatch, )); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 5ad15feadff..27b45f70946 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -5312,9 +5312,10 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( }; let name = tcx.item_name(proj.projection_term.def_id); let mut predicates = generics.predicates.iter().peekable(); - let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None; + let mut prev: Option<(&hir::WhereBoundPredicate<'_>, Span)> = None; while let Some(pred) = predicates.next() { - let hir::WherePredicate::BoundPredicate(pred) = pred else { + let curr_span = pred.span; + let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind else { continue; }; let mut bounds = pred.bounds.iter(); @@ -5340,8 +5341,8 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( .iter() .filter(|p| { matches!( - p, - hir::WherePredicate::BoundPredicate(p) + p.kind, + hir::WherePredicateKind::BoundPredicate(p) if hir::PredicateOrigin::WhereClause == p.origin ) }) @@ -5351,20 +5352,21 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( // There's only one `where` bound, that needs to be removed. Remove the whole // `where` clause. generics.where_clause_span - } else if let Some(hir::WherePredicate::BoundPredicate(next)) = predicates.peek() + } else if let Some(next_pred) = predicates.peek() + && let hir::WherePredicateKind::BoundPredicate(next) = next_pred.kind && pred.origin == next.origin { // There's another bound, include the comma for the current one. - pred.span.until(next.span) - } else if let Some(prev) = prev + curr_span.until(next_pred.span) + } else if let Some((prev, prev_span)) = prev && pred.origin == prev.origin { // Last bound, try to remove the previous comma. - prev.span.shrink_to_hi().to(pred.span) + prev_span.shrink_to_hi().to(curr_span) } else if pred.origin == hir::PredicateOrigin::WhereClause { - pred.span.with_hi(generics.where_clause_span.hi()) + curr_span.with_hi(generics.where_clause_span.hi()) } else { - pred.span + curr_span }; err.span_suggestion_verbose( @@ -5417,7 +5419,7 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>( ); } } - prev = Some(pred); + prev = Some((pred, curr_span)); } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 2cc787c8bc5..09bba24ba61 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -204,7 +204,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - match self.typing_mode_unchecked() { + match self.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { .. } => false, TypingMode::PostAnalysis => { let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref); diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 53e5420d27a..0f90c45d032 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -264,14 +264,14 @@ fn fulfillment_error_for_no_solution<'tcx>( let (a, b) = infcx.enter_forall_and_leak_universe( obligation.predicate.kind().rebind((pred.a, pred.b)), ); - let expected_found = ExpectedFound::new(true, a, b); + let expected_found = ExpectedFound::new(a, b); FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) } ty::PredicateKind::Coerce(pred) => { let (a, b) = infcx.enter_forall_and_leak_universe( obligation.predicate.kind().rebind((pred.a, pred.b)), ); - let expected_found = ExpectedFound::new(false, a, b); + let expected_found = ExpectedFound::new(b, a); FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) } ty::PredicateKind::Clause(_) @@ -346,12 +346,21 @@ fn find_best_leaf_obligation<'tcx>( consider_ambiguities: bool, ) -> PredicateObligation<'tcx> { let obligation = infcx.resolve_vars_if_possible(obligation.clone()); + // FIXME: we use a probe here as the `BestObligation` visitor does not + // check whether it uses candidates which get shadowed by where-bounds. + // + // We should probably fix the visitor to not do so instead, as this also + // means the leaf obligation may be incorrect. infcx - .visit_proof_tree(obligation.clone().into(), &mut BestObligation { - obligation: obligation.clone(), - consider_ambiguities, + .fudge_inference_if_ok(|| { + infcx + .visit_proof_tree(obligation.clone().into(), &mut BestObligation { + obligation: obligation.clone(), + consider_ambiguities, + }) + .break_value() + .ok_or(()) }) - .break_value() .unwrap_or(obligation) } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index fac0414d714..6730f28893d 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -318,13 +318,11 @@ impl<'tcx> AutoTraitFinder<'tcx> { elaborate(tcx, computed_preds.clone().chain(user_computed_preds.iter().cloned())); new_env = ty::ParamEnv::new( tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())), - param_env.reveal(), ); } let final_user_env = ty::ParamEnv::new( tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())), - user_env.reveal(), ); debug!( "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \ diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index cf63f14fb93..e0a9ddf1876 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -150,8 +150,8 @@ fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> .predicates .iter() .filter_map(|pred| { - match pred { - hir::WherePredicate::BoundPredicate(pred) + match pred.kind { + hir::WherePredicateKind::BoundPredicate(pred) if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id => { // Fetch spans for trait bounds that are Sized: @@ -707,7 +707,7 @@ fn receiver_is_dispatchable<'tcx>( let caller_bounds = param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]); - ty::ParamEnv::new(tcx.mk_clauses_from_iter(caller_bounds), param_env.reveal()) + ty::ParamEnv::new(tcx.mk_clauses_from_iter(caller_bounds)) }; // Receiver: DispatchFromDyn<Receiver[Self => U]> diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index cb36f1a62db..07fb2efb7fe 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -20,13 +20,18 @@ pub fn evaluate_host_effect_obligation<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, ) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { - if matches!(selcx.infcx.typing_mode(obligation.param_env), TypingMode::Coherence) { + if matches!(selcx.infcx.typing_mode(), TypingMode::Coherence) { span_bug!( obligation.cause.span, "should not select host obligation in old solver in intercrate mode" ); } + // Force ambiguity for infer self ty. + if obligation.predicate.self_ty().is_ty_var() { + return Err(EvaluationFailure::Ambiguous); + } + match evaluate_host_effect_from_bounds(selcx, obligation) { Ok(result) => return Ok(result), Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 2ec5f0d2249..03e483f555d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -557,8 +557,11 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ProcessResult::Changed(mk_pending(ok.obligations)) } Ok(Err(err)) => { - let expected_found = - ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b); + let expected_found = if subtype.a_is_expected { + ExpectedFound::new(subtype.a, subtype.b) + } else { + ExpectedFound::new(subtype.b, subtype.a) + }; ProcessResult::Error(FulfillmentErrorCode::Subtype(expected_found, err)) } } @@ -578,7 +581,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), Ok(Err(err)) => { - let expected_found = ExpectedFound::new(false, coerce.a, coerce.b); + let expected_found = ExpectedFound::new(coerce.b, coerce.a); ProcessResult::Error(FulfillmentErrorCode::Subtype(expected_found, err)) } } @@ -703,7 +706,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } Err(err) => { ProcessResult::Error(FulfillmentErrorCode::ConstEquate( - ExpectedFound::new(true, c1, c2), + ExpectedFound::new(c1, c2), err, )) } @@ -727,7 +730,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ProcessResult::Unchanged } else { // Two different constants using generic parameters ~> error. - let expected_found = ExpectedFound::new(true, c1, c2); + let expected_found = ExpectedFound::new(c1, c2); ProcessResult::Error(FulfillmentErrorCode::ConstEquate( expected_found, TypeError::ConstMismatch(expected_found), @@ -768,8 +771,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { stalled_on: &mut Vec<TyOrConstInferVar>, ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { let infcx = self.selcx.infcx; - if obligation.predicate.is_global() - && !matches!(infcx.typing_mode(obligation.param_env), TypingMode::Coherence) + if obligation.predicate.is_global() && !matches!(infcx.typing_mode(), TypingMode::Coherence) { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. @@ -824,8 +826,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { let tcx = self.selcx.tcx(); let infcx = self.selcx.infcx; - if obligation.predicate.is_global() - && !matches!(infcx.typing_mode(obligation.param_env), TypingMode::Coherence) + if obligation.predicate.is_global() && !matches!(infcx.typing_mode(), TypingMode::Coherence) { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 80cef690028..069fab6a6e6 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -423,7 +423,7 @@ pub fn normalize_param_env_or_error<'tcx>( debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); - let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal()); + let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates)); if !elaborated_env.has_aliases() { return elaborated_env; } @@ -470,8 +470,7 @@ pub fn normalize_param_env_or_error<'tcx>( // here. I believe they should not matter, because we are ignoring TypeOutlives param-env // predicates here anyway. Keeping them here anyway because it seems safer. let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned(); - let outlives_env = - ty::ParamEnv::new(tcx.mk_clauses_from_iter(outlives_env), unnormalized_env.reveal()); + let outlives_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(outlives_env)); let Ok(outlives_predicates) = do_normalize_predicates(tcx, cause, outlives_env, outlives_predicates) else { @@ -484,7 +483,7 @@ pub fn normalize_param_env_or_error<'tcx>( let mut predicates = non_outlives_predicates; predicates.extend(outlives_predicates); debug!("normalize_param_env_or_error: final predicates={:?}", predicates); - ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal()) + ty::ParamEnv::new(tcx.mk_clauses(&predicates)) } #[derive(Debug)] @@ -552,8 +551,18 @@ pub fn try_evaluate_const<'tcx>( | ty::ConstKind::Placeholder(_) | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), ty::ConstKind::Unevaluated(uv) => { - // Postpone evaluation of constants that depend on generic parameters or inference variables. - let (args, param_env) = if tcx.features().generic_const_exprs() + // Postpone evaluation of constants that depend on generic parameters or + // inference variables. + // + // We use `TypingMode::PostAnalysis` here which is not *technically* correct + // to be revealing opaque types here as borrowcheck has not run yet. However, + // CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during + // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). + // As a result we always use a revealed env when resolving the instance to evaluate. + // + // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself + // instead of having this logic here + let (args, typing_env) = if tcx.features().generic_const_exprs() && uv.has_non_region_infer() { // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause @@ -569,13 +578,17 @@ pub fn try_evaluate_const<'tcx>( // the generic arguments provided for it, then we should *not* attempt to evaluate it. return Err(EvaluateConstErr::HasGenericsOrInfers); } else { - (replace_param_and_infer_args_with_placeholder(tcx, uv.args), param_env) + let args = replace_param_and_infer_args_with_placeholder(tcx, uv.args); + let typing_env = infcx + .typing_env(tcx.erase_regions(param_env)) + .with_post_analysis_normalized(tcx); + (args, typing_env) } } Err(_) | Ok(None) => { let args = GenericArgs::identity_for_item(tcx, uv.def); - let param_env = tcx.param_env(uv.def); - (args, param_env) + let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); + (args, typing_env) } } } else if tcx.def_kind(uv.def) == DefKind::AnonConst && uv.has_non_region_infer() { @@ -594,27 +607,20 @@ pub fn try_evaluate_const<'tcx>( ); let args = GenericArgs::identity_for_item(tcx, uv.def); - let param_env = tcx.param_env(uv.def); - (args, param_env) + let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); + (args, typing_env) } else { // FIXME: This codepath is reachable under `associated_const_equality` and in the // future will be reachable by `min_generic_const_args`. We should handle inference // variables and generic parameters properly instead of doing nothing. - (uv.args, param_env) + let typing_env = infcx + .typing_env(tcx.erase_regions(param_env)) + .with_post_analysis_normalized(tcx); + (uv.args, typing_env) }; let uv = ty::UnevaluatedConst::new(uv.def, args); - // It's not *technically* correct to be revealing opaque types here as we could still be - // before borrowchecking. However, CTFE itself uses `Reveal::All` unconditionally even during - // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). As a result we - // always use a revealed env when resolving the instance to evaluate. - // - // FIXME: `const_eval_resolve_for_typeck` should probably just set the env to `Reveal::All` - // instead of having this logic here - let typing_env = - tcx.erase_regions(infcx.typing_env(param_env)).with_reveal_all_normalized(tcx); let erased_uv = tcx.erase_regions(uv); - use rustc_middle::mir::interpret::ErrorHandled; match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) { Ok(Ok(val)) => Ok(ty::Const::new_value( diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 5c38d162712..4d3d8c66e62 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -111,14 +111,13 @@ where pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( infcx: &InferCtxt<'tcx>, - param_env_for_debug_assertion: ty::ParamEnv<'tcx>, value: &T, ) -> bool { let mut flags = ty::TypeFlags::HAS_ALIAS; - // Opaques are treated as rigid with `Reveal::UserFacing`, + // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`, // so we can ignore those. - match infcx.typing_mode(param_env_for_debug_assertion) { + match infcx.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { defining_opaque_types: _ } => { flags.remove(ty::TypeFlags::HAS_TY_OPAQUE) } @@ -158,11 +157,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { "Normalizing {value:?} without wrapping in a `Binder`" ); - if !needs_normalization(self.selcx.infcx, self.param_env, &value) { - value - } else { - value.fold_with(self) - } + if !needs_normalization(self.selcx.infcx, &value) { value } else { value.fold_with(self) } } } @@ -182,7 +177,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !needs_normalization(self.selcx.infcx, self.param_env, &ty) { + if !needs_normalization(self.selcx.infcx, &ty) { return ty; } @@ -217,7 +212,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx match kind { ty::Opaque => { // Only normalize `impl Trait` outside of type inference, usually in codegen. - match self.selcx.infcx.typing_mode(self.param_env) { + match self.selcx.infcx.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { defining_opaque_types: _ } => { ty.super_fold_with(self) } @@ -407,8 +402,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx #[instrument(skip(self), level = "debug")] fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs() - || !needs_normalization(self.selcx.infcx, self.param_env, &constant) + if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &constant) { constant } else { @@ -426,7 +420,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx #[inline] fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { - if p.allow_normalization() && needs_normalization(self.selcx.infcx, self.param_env, &p) { + if p.allow_normalization() && needs_normalization(self.selcx.infcx, &p) { p.super_fold_with(self) } else { p diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index aab854e9caf..2864f277df5 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -10,7 +10,6 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; -pub use rustc_middle::traits::Reveal; use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; @@ -975,7 +974,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - match selcx.infcx.typing_mode(obligation.param_env) { + match selcx.infcx.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { .. } => { debug!( assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 5f89894fb82..22cfbb2c840 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -89,7 +89,7 @@ impl<'a, 'tcx> At<'a, 'tcx> { } } - if !needs_normalization(self.infcx, self.param_env, &value) { + if !needs_normalization(self.infcx, &value) { return Ok(Normalized { value, obligations: PredicateObligations::new() }); } @@ -191,7 +191,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { - if !needs_normalization(self.infcx, self.param_env, &ty) { + if !needs_normalization(self.infcx, &ty) { return Ok(ty); } @@ -215,7 +215,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> { let res = match kind { ty::Opaque => { // Only normalize `impl Trait` outside of type inference, usually in codegen. - match self.infcx.typing_mode(self.param_env) { + match self.infcx.typing_mode() { TypingMode::Coherence | TypingMode::Analysis { defining_opaque_types: _ } => { ty.try_super_fold_with(self)? } @@ -334,7 +334,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> { &mut self, constant: ty::Const<'tcx>, ) -> Result<ty::Const<'tcx>, Self::Error> { - if !needs_normalization(self.infcx, self.param_env, &constant) { + if !needs_normalization(self.infcx, &constant) { return Ok(constant); } @@ -353,7 +353,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> { &mut self, p: ty::Predicate<'tcx>, ) -> Result<ty::Predicate<'tcx>, Self::Error> { - if p.allow_normalization() && needs_normalization(self.infcx, self.param_env, &p) { + if p.allow_normalization() && needs_normalization(self.infcx, &p) { p.try_super_fold_with(self) } else { Ok(p) diff --git a/compiler/rustc_trait_selection/src/traits/select/_match.rs b/compiler/rustc_trait_selection/src/traits/select/_match.rs index 3980d672a11..7c19c35a4f7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/_match.rs +++ b/compiler/rustc_trait_selection/src/traits/select/_match.rs @@ -70,7 +70,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> { ) => Ok(a), (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) + Err(TypeError::Sorts(ExpectedFound::new(a, b))) } (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.cx(), guar)), @@ -95,7 +95,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> { } (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => { - return Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b))); + return Err(TypeError::ConstMismatch(ExpectedFound::new(a, b))); } _ => {} diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 345e1cc31f3..32b4567aba4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -91,14 +91,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else if tcx.is_lang_item(def_id, LangItem::Sized) { // Sized is never implementable by end-users, it is // always automatically computed. - - // FIXME: Consider moving this check to the top level as it - // may also be useful for predicates other than `Sized` - // Error type cannot possibly implement `Sized` (fixes #123154) - if let Err(e) = obligation.predicate.skip_binder().self_ty().error_reported() { - return Err(SelectionError::Overflow(e.into())); - } - let sized_conditions = self.sized_conditions(obligation); self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); } else if tcx.is_lang_item(def_id, LangItem::Unsize) { @@ -230,13 +222,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result<(), SelectionError<'tcx>> { debug!(?stack.obligation); - // An error type will unify with anything. So, avoid - // matching an error type with `ParamCandidate`. - // This helps us avoid spurious errors like issue #121941. - if stack.obligation.predicate.references_error() { - return Ok(()); - } - let bounds = stack .obligation .param_env @@ -563,19 +548,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - // Essentially any user-written impl will match with an error type, - // so creating `ImplCandidates` isn't useful. However, we might - // end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized`) - // This helps us avoid overflow: see issue #72839 - // Since compilation is already guaranteed to fail, this is just - // to try to show the 'nicest' possible errors to the user. - // We don't check for errors in the `ParamEnv` - in practice, - // it seems to cause us to be overly aggressive in deciding - // to give up searching for candidates, leading to spurious errors. - if obligation.predicate.references_error() { - return; - } - let drcx = DeepRejectCtxt::relate_rigid_infer(self.tcx()); let obligation_args = obligation.predicate.skip_binder().trait_ref.args; self.tcx().for_each_relevant_impl( @@ -788,9 +760,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // Note that this is only sound as projection candidates of opaque types // are always applicable for auto traits. - } else if let TypingMode::Coherence = - self.infcx.typing_mode(obligation.param_env) - { + } else if let TypingMode::Coherence = self.infcx.typing_mode() { // We do not emit auto trait candidates for opaque types in coherence. // Doing so can result in weird dependency cycles. candidates.ambiguous = true; @@ -933,7 +903,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // FIXME(@lcnr): This should probably only trigger during analysis, // disabling candidates during codegen is also questionable. - if let TypingMode::Coherence = self.infcx.typing_mode(param_env) { + if let TypingMode::Coherence = self.infcx.typing_mode() { return None; } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5b4e895189b..3b64a47181a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -222,7 +222,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Enables tracking of intercrate ambiguity causes. See /// the documentation of [`Self::intercrate_ambiguity_causes`] for more. pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { - assert_matches!(self.infcx.typing_mode_unchecked(), TypingMode::Coherence); + assert_matches!(self.infcx.typing_mode(), TypingMode::Coherence); assert!(self.intercrate_ambiguity_causes.is_none()); self.intercrate_ambiguity_causes = Some(FxIndexSet::default()); debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); @@ -234,7 +234,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn take_intercrate_ambiguity_causes( &mut self, ) -> FxIndexSet<IntercrateAmbiguityCause<'tcx>> { - assert_matches!(self.infcx.typing_mode_unchecked(), TypingMode::Coherence); + assert_matches!(self.infcx.typing_mode(), TypingMode::Coherence); self.intercrate_ambiguity_causes.take().unwrap_or_default() } @@ -1027,7 +1027,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { previous_stack: TraitObligationStackList<'o, 'tcx>, mut obligation: PolyTraitObligation<'tcx>, ) -> Result<EvaluationResult, OverflowError> { - if !matches!(self.infcx.typing_mode(obligation.param_env), TypingMode::Coherence) + if !matches!(self.infcx.typing_mode(), TypingMode::Coherence) && obligation.is_global() && obligation.param_env.caller_bounds().iter().all(|bound| bound.has_param()) { @@ -1310,13 +1310,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option<EvaluationResult> { - let tcx = self.tcx(); + let infcx = self.infcx; + let tcx = infcx.tcx; if self.can_use_global_caches(param_env, trait_pred) { - if let Some(res) = tcx.evaluation_cache.get(&(param_env, trait_pred), tcx) { - return Some(res); + let key = (infcx.typing_env(param_env), trait_pred); + if let Some(res) = tcx.evaluation_cache.get(&key, tcx) { + Some(res) + } else { + debug_assert_eq!(infcx.evaluation_cache.get(&(param_env, trait_pred), tcx), None); + None } + } else { + self.infcx.evaluation_cache.get(&(param_env, trait_pred), tcx) } - self.infcx.evaluation_cache.get(&(param_env, trait_pred), tcx) } fn insert_evaluation_cache( @@ -1332,18 +1338,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - if self.can_use_global_caches(param_env, trait_pred) && !trait_pred.has_infer() { + let infcx = self.infcx; + let tcx = infcx.tcx; + if self.can_use_global_caches(param_env, trait_pred) { debug!(?trait_pred, ?result, "insert_evaluation_cache global"); // This may overwrite the cache with the same value - // FIXME: Due to #50507 this overwrites the different values - // This should be changed to use HashMapExt::insert_same - // when that is fixed - self.tcx().evaluation_cache.insert((param_env, trait_pred), dep_node, result); + tcx.evaluation_cache.insert( + (infcx.typing_env(param_env), trait_pred), + dep_node, + result, + ); return; + } else { + debug!(?trait_pred, ?result, "insert_evaluation_cache local"); + self.infcx.evaluation_cache.insert((param_env, trait_pred), dep_node, result); } - - debug!(?trait_pred, ?result, "insert_evaluation_cache"); - self.infcx.evaluation_cache.insert((param_env, trait_pred), dep_node, result); } fn check_recursion_depth<T>( @@ -1459,7 +1468,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> { let obligation = &stack.obligation; - match self.infcx.typing_mode(obligation.param_env) { + match self.infcx.typing_mode() { TypingMode::Coherence => {} TypingMode::Analysis { .. } | TypingMode::PostAnalysis => return Ok(()), } @@ -1485,11 +1494,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If there are any inference variables in the `ParamEnv`, then we // always use a cache local to this particular scope. Otherwise, we // switch to a global cache. - if param_env.has_infer() { + if param_env.has_infer() || pred.has_infer() { return false; } - match self.infcx.typing_mode(param_env) { + match self.infcx.typing_mode() { // Avoid using the global cache during coherence and just rely // on the local cache. It is really just a simplification to // avoid us having to fear that coherence results "pollute" @@ -1522,15 +1531,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>> { - let tcx = self.tcx(); + let infcx = self.infcx; + let tcx = infcx.tcx; let pred = cache_fresh_trait_pred.skip_binder(); if self.can_use_global_caches(param_env, cache_fresh_trait_pred) { - if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) { - return Some(res); + if let Some(res) = tcx.selection_cache.get(&(infcx.typing_env(param_env), pred), tcx) { + Some(res) + } else { + debug_assert_eq!(infcx.selection_cache.get(&(param_env, pred), tcx), None); + None } + } else { + infcx.selection_cache.get(&(param_env, pred), tcx) } - self.infcx.selection_cache.get(&(param_env, pred), tcx) } /// Determines whether can we safely cache the result @@ -1567,7 +1581,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { dep_node: DepNodeIndex, candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, ) { - let tcx = self.tcx(); + let infcx = self.infcx; + let tcx = infcx.tcx; let pred = cache_fresh_trait_pred.skip_binder(); if !self.can_cache_candidate(&candidate) { @@ -1578,10 +1593,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.can_use_global_caches(param_env, cache_fresh_trait_pred) { if let Err(Overflow(OverflowError::Canonical)) = candidate { // Don't cache overflow globally; we only produce this in certain modes. - } else if !pred.has_infer() && !candidate.has_infer() { + } else { debug!(?pred, ?candidate, "insert_candidate_cache global"); + debug_assert!(!candidate.has_infer()); + // This may overwrite the cache with the same value. - tcx.selection_cache.insert((param_env, pred), dep_node, candidate); + tcx.selection_cache.insert( + (infcx.typing_env(param_env), pred), + dep_node, + candidate, + ); return; } } @@ -2487,10 +2508,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let impl_args = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id); let trait_ref = impl_trait_header.trait_ref.instantiate(self.tcx(), impl_args); - if trait_ref.references_error() { - return Err(()); - } - debug!(?impl_trait_header); let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = @@ -2522,7 +2539,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { nested_obligations.extend(obligations); if impl_trait_header.polarity == ty::ImplPolarity::Reservation - && !matches!(self.infcx.typing_mode(obligation.param_env), TypingMode::Coherence) + && !matches!(self.infcx.typing_mode(), TypingMode::Coherence) { debug!("reservation impls only apply in intercrate mode"); return Err(()); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 5bf3dbcbc32..a9cd705465e 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -15,24 +15,24 @@ 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::infer::DefineOpaqueTypes; use rustc_middle::bug; use rustc_middle::query::LocalCrate; use rustc_middle::ty::print::PrintTraitRefExt as _; -use rustc_middle::ty::{ - self, GenericArgsRef, ImplSubject, Ty, TyCtxt, TypeVisitableExt, TypingMode, -}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_session::lint::builtin::{COHERENCE_LEAK_CHECK, ORDER_DEPENDENT_TRAIT_OBJECTS}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, sym}; +use rustc_type_ir::solve::NoSolution; use specialization_graph::GraphExt; use tracing::{debug, instrument}; -use super::{SelectionContext, util}; use crate::error_reporting::traits::to_pretty_impl_header; use crate::errors::NegativePositiveConflict; -use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; -use crate::traits::{FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, coherence}; +use crate::traits::{ + FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, coherence, + predicates_for_generics, +}; /// Information pertinent to an overlapping impl error. #[derive(Debug)] @@ -87,9 +87,14 @@ pub fn translate_args<'tcx>( source_args: GenericArgsRef<'tcx>, target_node: specialization_graph::Node, ) -> GenericArgsRef<'tcx> { - translate_args_with_cause(infcx, param_env, source_impl, source_args, target_node, |_, _| { - ObligationCause::dummy() - }) + translate_args_with_cause( + infcx, + param_env, + source_impl, + source_args, + target_node, + &ObligationCause::dummy(), + ) } /// Like [translate_args], but obligations from the parent implementation @@ -104,7 +109,7 @@ pub fn translate_args_with_cause<'tcx>( source_impl: DefId, source_args: GenericArgsRef<'tcx>, target_node: specialization_graph::Node, - cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, + cause: &ObligationCause<'tcx>, ) -> GenericArgsRef<'tcx> { debug!( "translate_args({:?}, {:?}, {:?}, {:?})", @@ -123,7 +128,7 @@ pub fn translate_args_with_cause<'tcx>( } fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause) - .unwrap_or_else(|()| { + .unwrap_or_else(|_| { bug!( "When translating generic parameters from {source_impl:?} to \ {target_impl:?}, the expected specialization failed to hold" @@ -137,6 +142,84 @@ pub fn translate_args_with_cause<'tcx>( source_args.rebase_onto(infcx.tcx, source_impl, target_args) } +/// Attempt to fulfill all obligations of `target_impl` after unification with +/// `source_trait_ref`. If successful, returns the generic parameters for *all* the +/// generics of `target_impl`, including both those needed to unify with +/// `source_trait_ref` and those whose identity is determined via a where +/// clause in the impl. +fn fulfill_implication<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_trait_ref: ty::TraitRef<'tcx>, + source_impl: DefId, + target_impl: DefId, + cause: &ObligationCause<'tcx>, +) -> Result<GenericArgsRef<'tcx>, NoSolution> { + debug!( + "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", + param_env, source_trait_ref, target_impl + ); + + let ocx = ObligationCtxt::new(infcx); + let source_trait_ref = ocx.normalize(cause, param_env, source_trait_ref); + + if !ocx.select_all_or_error().is_empty() { + infcx.dcx().span_delayed_bug( + infcx.tcx.def_span(source_impl), + format!("failed to fully normalize {source_trait_ref}"), + ); + return Err(NoSolution); + } + + let target_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl); + let target_trait_ref = ocx.normalize( + cause, + param_env, + infcx + .tcx + .impl_trait_ref(target_impl) + .expect("expected source impl to be a trait impl") + .instantiate(infcx.tcx, target_args), + ); + + // do the impls unify? If not, no specialization. + ocx.eq(cause, param_env, source_trait_ref, target_trait_ref)?; + + // 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(target_impl).instantiate(infcx.tcx, target_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 {:?}", + source_trait_ref, + target_trait_ref, + errors, + param_env.caller_bounds() + ); + return Err(NoSolution); + } + + debug!( + "fulfill_implication: an impl for {:?} specializes {:?}", + source_trait_ref, target_trait_ref + ); + + // Now resolve the *generic parameters* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_vars_if_possible(target_args)) +} + pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool { tcx.features().specialization() || tcx.features().min_specialization() } @@ -182,8 +265,9 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, return false; } - // create a parameter environment corresponding to a (placeholder) instantiation of impl1 - let penv = tcx.param_env(impl1_def_id); + // 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 an infcx, taking the predicates of impl1 as assumptions: let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); @@ -191,90 +275,15 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, // Attempt to prove that impl2 applies, given all of the above. fulfill_implication( &infcx, - penv, + param_env, impl1_trait_header.trait_ref.instantiate_identity(), impl1_def_id, impl2_def_id, - |_, _| ObligationCause::dummy(), + &ObligationCause::dummy(), ) .is_ok() } -/// Attempt to fulfill all obligations of `target_impl` after unification with -/// `source_trait_ref`. If successful, returns the generic parameters for *all* the -/// generics of `target_impl`, including both those needed to unify with -/// `source_trait_ref` and those whose identity is determined via a where -/// clause in the impl. -fn fulfill_implication<'tcx>( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - source_trait_ref: ty::TraitRef<'tcx>, - source_impl: DefId, - target_impl: DefId, - error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, -) -> Result<GenericArgsRef<'tcx>, ()> { - debug!( - "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", - param_env, source_trait_ref, target_impl - ); - - let ocx = ObligationCtxt::new(infcx); - let source_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, source_trait_ref); - - if !ocx.select_all_or_error().is_empty() { - infcx.dcx().span_delayed_bug( - infcx.tcx.def_span(source_impl), - format!("failed to fully normalize {source_trait_ref}"), - ); - } - - let source_trait_ref = infcx.resolve_vars_if_possible(source_trait_ref); - let source_trait = ImplSubject::Trait(source_trait_ref); - - let selcx = SelectionContext::new(infcx); - let target_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl); - let (target_trait, obligations) = - util::impl_subject_and_oblig(&selcx, param_env, target_impl, target_args, error_cause); - - // do the impls unify? If not, no specialization. - let Ok(InferOk { obligations: more_obligations, .. }) = infcx - .at(&ObligationCause::dummy(), param_env) - // Ok to use `Yes`, as all the generic params are already replaced by inference variables, - // which will match the opaque type no matter if it is defining or not. - // Any concrete type that would match the opaque would already be handled by coherence rules, - // and thus either be ok to match here and already have errored, or it won't match, in which - // case there is no issue anyway. - .eq(DefineOpaqueTypes::Yes, source_trait, target_trait) - else { - debug!("fulfill_implication: {:?} does not unify with {:?}", source_trait, target_trait); - return Err(()); - }; - - // attempt to prove all of the predicates for impl2 given those for impl1 - // (which are packed up in penv) - ocx.register_obligations(obligations.chain(more_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 {:?}", - source_trait, - target_trait, - errors, - param_env.caller_bounds() - ); - return Err(()); - } - - debug!("fulfill_implication: an impl for {:?} specializes {:?}", source_trait, target_trait); - - // Now resolve the *generic parameters* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. - Ok(infcx.resolve_vars_if_possible(target_args)) -} - /// Query provider for `specialization_graph_of`. pub(super) fn specialization_graph_provider( tcx: TyCtxt<'_>, diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index b7a2f20b769..da1045b664a 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -3,19 +3,16 @@ use std::collections::BTreeMap; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Diag; use rustc_hir::def_id::DefId; -use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::infer::InferCtxt; pub use rustc_infer::traits::util::*; use rustc_middle::bug; use rustc_middle::ty::{ - self, GenericArgsRef, ImplSubject, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, Upcast, + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, }; use rustc_span::Span; use smallvec::{SmallVec, smallvec}; use tracing::debug; -use super::{NormalizeExt, ObligationCause, PredicateObligation, SelectionContext}; - /////////////////////////////////////////////////////////////////////////// // `TraitAliasExpander` iterator /////////////////////////////////////////////////////////////////////////// @@ -166,34 +163,6 @@ impl<'tcx> Iterator for TraitAliasExpander<'tcx> { // Other /////////////////////////////////////////////////////////////////////////// -/// Instantiate all bound parameters of the impl subject with the given args, -/// returning the resulting subject and all obligations that arise. -/// The obligations are closed under normalization. -pub(crate) fn impl_subject_and_oblig<'a, 'tcx>( - selcx: &SelectionContext<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - impl_def_id: DefId, - impl_args: GenericArgsRef<'tcx>, - cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, -) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) { - let subject = selcx.tcx().impl_subject(impl_def_id); - let subject = subject.instantiate(selcx.tcx(), impl_args); - - let InferOk { value: subject, obligations: normalization_obligations1 } = - selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(subject); - - let predicates = selcx.tcx().predicates_of(impl_def_id); - let predicates = predicates.instantiate(selcx.tcx(), impl_args); - let InferOk { value: predicates, obligations: normalization_obligations2 } = - selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates); - let impl_obligations = super::predicates_for_generics(cause, param_env, predicates); - - let impl_obligations = - impl_obligations.chain(normalization_obligations1).chain(normalization_obligations2); - - (subject, impl_obligations) -} - /// Casts a trait reference into a reference to one of its super /// traits; returns `None` if `target_trait_def_id` is not a /// supertrait. diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 437343b569c..c95b1641d1f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -963,30 +963,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { /// bounds that must hold on the elided self type. These are derived /// from the declarations of `SomeTrait`, `Send`, and friends -- if /// they declare `trait SomeTrait : 'static`, for example, then -/// `'static` would appear in the list. The hard work is done by -/// `infer::required_region_bounds`, see that for more information. -pub fn object_region_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, -) -> Vec<ty::Region<'tcx>> { - let predicates = existential_predicates.iter().filter_map(|predicate| { - if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() { - None - } else { - Some(predicate.with_self_ty(tcx, tcx.types.trait_object_dummy_self)) - } - }); - - required_region_bounds(tcx, tcx.types.trait_object_dummy_self, predicates) -} - -/// Given a set of predicates that apply to an object type, returns -/// the region bounds that the (erased) `Self` type must -/// outlive. Precisely *because* the `Self` type is erased, the -/// parameter `erased_self_ty` must be supplied to indicate what type -/// has been used to represent `Self` in the predicates -/// themselves. This should really be a unique type; `FreshTy(0)` is a -/// popular choice. +/// `'static` would appear in the list. /// /// N.B., in some cases, particularly around higher-ranked bounds, /// this function returns a kind of conservative approximation. @@ -996,13 +973,14 @@ pub fn object_region_bounds<'tcx>( /// /// Requires that trait definitions have been processed so that we can /// elaborate predicates and walk supertraits. -#[instrument(skip(tcx, predicates), level = "debug", ret)] -pub(crate) fn required_region_bounds<'tcx>( +pub fn object_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, - erased_self_ty: Ty<'tcx>, - predicates: impl Iterator<Item = ty::Clause<'tcx>>, + existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Vec<ty::Region<'tcx>> { - assert!(!erased_self_ty.has_escaping_bound_vars()); + let erased_self_ty = tcx.types.trait_object_dummy_self; + + let predicates = + existential_predicates.iter().map(|predicate| predicate.with_self_ty(tcx, erased_self_ty)); traits::elaborate(tcx, predicates) .filter_map(|pred| { |
