From 488d0c9efd355f712db4ea34856ddbeea26a7049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 8 Dec 2022 13:31:21 +0100 Subject: [PATCH 01/11] Type-directed probing for inherent associated types --- .../rustc_hir_analysis/src/astconv/errors.rs | 166 ++++++++++- .../rustc_hir_analysis/src/astconv/mod.rs | 257 ++++++++++++++---- .../dispatch-on-self-type-0.rs | 35 +++ .../dispatch-on-self-type-1.rs | 39 +++ .../dispatch-on-self-type-2.rs | 17 ++ .../dispatch-on-self-type-2.stderr | 19 ++ .../not-found-self-type-differs.alias.stderr | 16 ++ .../not-found-self-type-differs.local.stderr | 16 ++ .../not-found-self-type-differs.rs | 22 ++ .../not-found-unsatisfied-bounds.rs | 21 ++ .../not-found-unsatisfied-bounds.stderr | 27 ++ 11 files changed, 581 insertions(+), 54 deletions(-) create mode 100644 tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs create mode 100644 tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs create mode 100644 tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs create mode 100644 tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.rs create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 232ef2079d6bb..191f4f0f910ad 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -1,10 +1,10 @@ use crate::astconv::AstConv; use crate::errors::{ManualImplementation, MissingTypeParams}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed}; +use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::parse::feature_err; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident}; @@ -221,6 +221,168 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.emit() } + // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. + pub(crate) fn complain_about_inherent_assoc_type_not_found( + &self, + name: Ident, + self_ty: Ty<'tcx>, + candidates: &[DefId], + unsatisfied_predicates: Vec>, + span: Span, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + + let adt_did = self_ty.ty_adt_def().map(|def| def.did()); + let add_def_label = |err: &mut Diagnostic| { + if let Some(did) = adt_did { + err.span_label( + tcx.def_span(did), + format!( + "associated item `{name}` not found for this {}", + tcx.def_kind(did).descr(did) + ), + ); + } + }; + + if unsatisfied_predicates.is_empty() { + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + + let limit = if candidates.len() == 5 { 5 } else { 4 }; + let type_candidates = candidates + .iter() + .take(limit) + .map(|candidate| { + format!("- `{}`", tcx.at(span).type_of(candidate).subst_identity()) + }) + .collect::>() + .join("\n"); + let additional_types = if candidates.len() > limit { + format!("\nand {} more types", candidates.len() - limit) + } else { + String::new() + }; + + let mut err = struct_span_err!( + tcx.sess, + name.span, + E0220, + "associated type `{name}` not found for `{self_ty}` in the current scope" + ); + err.span_label(name.span, format!("associated item not found in `{self_ty}`")); + err.note(&format!( + "the associated type was found for\n{type_candidates}{additional_types}", + )); + add_def_label(&mut err); + return err.emit(); + } + + let mut bound_spans = Vec::new(); + + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { + let msg = format!( + "doesn't satisfy `{}`", + if obligation.len() > 50 { quiet } else { obligation } + ); + match &self_ty.kind() { + // Point at the type that couldn't satisfy the bound. + ty::Adt(def, _) => bound_spans.push((tcx.def_span(def.did()), msg)), + // Point at the trait object that couldn't satisfy the bound. + ty::Dynamic(preds, _, _) => { + for pred in preds.iter() { + match pred.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => { + bound_spans.push((tcx.def_span(tr.def_id), msg.clone())) + } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => {} + } + } + } + // Point at the closure that couldn't satisfy the bound. + ty::Closure(def_id, _) => { + bound_spans.push((tcx.def_span(*def_id), format!("doesn't satisfy `{quiet}`"))) + } + _ => {} + } + }; + + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + let format_pred = |pred: ty::Predicate<'tcx>| { + let bound_predicate = pred.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { + let pred = bound_predicate.rebind(pred); + // `::Item = String`. + let projection_ty = pred.skip_binder().projection_ty; + + let substs_with_infer_self = tcx.mk_substs( + std::iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into()) + .chain(projection_ty.substs.iter().skip(1)), + ); + + let quiet_projection_ty = + tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self); + + let term = pred.skip_binder().term; + + let obligation = format!("{projection_ty} = {term}"); + let quiet = format!("{quiet_projection_ty} = {term}"); + + bound_span_label(projection_ty.self_ty(), &obligation, &quiet); + Some((obligation, projection_ty.self_ty())) + } + ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => { + let p = poly_trait_ref.trait_ref; + let self_ty = p.self_ty(); + let path = p.print_only_trait_path(); + let obligation = format!("{self_ty}: {path}"); + let quiet = format!("_: {path}"); + bound_span_label(self_ty, &obligation, &quiet); + Some((obligation, self_ty)) + } + _ => None, + } + }; + + // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds. + // I would do the same here if it didn't mean more code duplication. + let mut bounds: Vec<_> = unsatisfied_predicates + .into_iter() + .filter_map(format_pred) + .map(|(p, _)| format!("`{}`", p)) + .collect(); + bounds.sort(); + bounds.dedup(); + + let mut err = tcx.sess.struct_span_err( + name.span, + &format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") + ); + if !bounds.is_empty() { + err.note(&format!( + "the following trait bounds were not satisfied:\n{}", + bounds.join("\n") + )); + } + err.span_label( + name.span, + format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") + ); + + bound_spans.sort(); + bound_spans.dedup(); + for (span, msg) in bound_spans { + if !tcx.sess.source_map().is_span_accessible(span) { + continue; + } + err.span_label(span, &msg); + } + add_def_label(&mut err); + err.emit() + } + /// When there are any missing associated types, emit an E0191 error and attempt to supply a /// reasonable suggestion on how to write it. For the case of multiple associated types in the /// same trait bound have the same name (as they come from different supertraits), we instead diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 2ff47237b1bf4..92bff68cdbce3 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -27,7 +27,10 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::traits::ObligationCause; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; use rustc_middle::ty::DynKind; @@ -40,11 +43,12 @@ use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::astconv_object_safety_violations; use rustc_trait_selection::traits::error_reporting::{ report_object_safety_error, suggestions::NextTypeParamName, }; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; +use rustc_trait_selection::traits::{astconv_object_safety_violations, NormalizeExt}; use smallvec::{smallvec, SmallVec}; use std::collections::BTreeSet; @@ -2043,23 +2047,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - // see if we can satisfy using an inherent associated type - for &impl_ in tcx.inherent_impls(adt_def.did()) { - let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else { - continue; - }; - let ty::Adt(_, adt_substs) = qself_ty.kind() else { - // FIXME(inherent_associated_types) - bug!("unimplemented: non-adt self of inherent assoc ty"); - }; - let item_substs = self.create_substs_for_associated_item( - span, - assoc_ty_did, - assoc_segment, - adt_substs, - ); - let ty = tcx.type_of(assoc_ty_did).subst(tcx, item_substs); - return Ok((ty, DefKind::AssocTy, assoc_ty_did)); + if let Some((ty, did)) = self.lookup_inherent_assoc_ty( + assoc_ident, + assoc_segment, + adt_def.did(), + qself_ty, + hir_ref_id, + span, + )? { + return Ok((ty, DefKind::AssocTy, did)); } } @@ -2202,6 +2198,196 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Ok((ty, DefKind::AssocTy, assoc_ty_did)) } + fn lookup_inherent_assoc_ty( + &self, + name: Ident, + segment: &hir::PathSegment<'_>, + adt_did: DefId, + self_ty: Ty<'tcx>, + block: hir::HirId, + span: Span, + ) -> Result, DefId)>, ErrorGuaranteed> { + let tcx = self.tcx(); + + let candidates: Vec<_> = tcx + .inherent_impls(adt_did) + .iter() + .filter_map(|&impl_| Some((impl_, self.lookup_assoc_ty_unchecked(name, block, impl_)?))) + .collect(); + + if candidates.is_empty() { + return Ok(None); + } + + let cause = ObligationCause::misc(span, block.owner.def_id); + let mut unsatisfied_predicates = Vec::new(); + + for &(impl_, (assoc_item, def_scope)) in &candidates { + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + let param_env = tcx.param_env(impl_); + + let impl_ty = tcx.type_of(impl_); + let impl_substs = self.fresh_item_substs(impl_, &infcx); + let impl_ty = impl_ty.subst(tcx, impl_substs); + + let InferOk { value: impl_ty, obligations } = + infcx.at(&cause, param_env).normalize(impl_ty); + + // Check that the Self-types can be related. + let Ok(InferOk { obligations: sub_obligations, value: () }) = infcx + .at(&ObligationCause::dummy(), param_env) + .define_opaque_types(false) + .sup(impl_ty, self_ty) + else { + continue; + }; + + // Check whether the impl imposes obligations we have to worry about. + let impl_bounds = tcx.predicates_of(impl_); + let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); + + let InferOk { value: impl_bounds, obligations: norm_obligations } = + infcx.at(&cause, param_env).normalize(impl_bounds); + + let impl_obligations = + traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); + + let candidate_obligations = impl_obligations + .chain(norm_obligations.into_iter()) + .chain(obligations.iter().cloned()); + + let mut matches = true; + + // Evaluate those obligations to see if they might possibly hold. + for o in candidate_obligations { + let o = infcx.resolve_vars_if_possible(o); + if !infcx.predicate_may_hold(&o) { + matches = false; + unsatisfied_predicates.push(o.predicate); + } + } + + // Evaluate those obligations to see if they might possibly hold. + for o in sub_obligations { + let o = infcx.resolve_vars_if_possible(o); + if !infcx.predicate_may_hold(&o) { + matches = false; + unsatisfied_predicates.push(o.predicate); + } + } + + if !matches { + continue; + } + + self.check_assoc_ty(assoc_item, name, def_scope, block, span); + + let ty::Adt(_, adt_substs) = self_ty.kind() else { + bug!("unreachable: `lookup_inherent_assoc_ty` is only called on ADTs"); + }; + + let item_substs = + self.create_substs_for_associated_item(span, assoc_item, segment, adt_substs); + // FIXME(inherent_associated_types): Check if the obligations arising from the + // where-clause & the bounds on the associated type and its parameters hold. + let ty = tcx.type_of(assoc_item).subst(tcx, item_substs); + return Ok(Some((ty, assoc_item))); + } + + Err(self.complain_about_inherent_assoc_type_not_found( + name, + self_ty, + &candidates.into_iter().map(|(impl_, _)| impl_).collect::>(), + unsatisfied_predicates, + span, + )) + } + + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + fn fresh_item_substs(&self, def_id: DefId, infcx: &InferCtxt<'tcx>) -> SubstsRef<'tcx> { + let tcx = self.tcx(); + + InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } => infcx + .next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::SubstitutionPlaceholder, + span: tcx.def_span(def_id), + }) + .into(), + GenericParamDefKind::Const { .. } => { + let span = tcx.def_span(def_id); + let origin = ConstVariableOrigin { + kind: ConstVariableOriginKind::SubstitutionPlaceholder, + span, + }; + infcx + .next_const_var( + tcx.type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + origin, + ) + .into() + } + }) + } + + fn lookup_assoc_ty( + &self, + name: Ident, + block: hir::HirId, + span: Span, + scope: DefId, + ) -> Option { + let (item, def_scope) = self.lookup_assoc_ty_unchecked(name, block, scope)?; + self.check_assoc_ty(item, name, def_scope, block, span); + Some(item) + } + + fn lookup_assoc_ty_unchecked( + &self, + name: Ident, + block: hir::HirId, + scope: DefId, + ) -> Option<(DefId, DefId)> { + let tcx = self.tcx(); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block); + + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead + // of calling `find_by_name_and_kind`. + let item = tcx.associated_items(scope).in_definition_order().find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == ident + })?; + + Some((item.def_id, def_scope)) + } + + fn check_assoc_ty( + &self, + item: DefId, + name: Ident, + def_scope: DefId, + block: hir::HirId, + span: Span, + ) { + let tcx = self.tcx(); + let kind = DefKind::AssocTy; + + if !tcx.visibility(item).is_accessible_from(def_scope, tcx) { + let kind = kind.descr(item); + let msg = format!("{kind} `{name}` is private"); + let def_span = tcx.def_span(item); + tcx.sess + .struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624)) + .span_label(span, &format!("private {kind}")) + .span_label(def_span, &format!("{kind} defined here")) + .emit(); + } + tcx.check_stability(item, Some(block), span, None); + } + fn probe_traits_that_match_assoc_ty( &self, qself_ty: Ty<'tcx>, @@ -2255,39 +2441,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .collect() } - fn lookup_assoc_ty( - &self, - ident: Ident, - block: hir::HirId, - span: Span, - scope: DefId, - ) -> Option { - let tcx = self.tcx(); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block); - - // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead - // of calling `find_by_name_and_kind`. - let item = tcx.associated_items(scope).in_definition_order().find(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident(tcx).normalize_to_macros_2_0() == ident - })?; - - let kind = DefKind::AssocTy; - if !item.visibility(tcx).is_accessible_from(def_scope, tcx) { - let kind = kind.descr(item.def_id); - let msg = format!("{kind} `{ident}` is private"); - let def_span = self.tcx().def_span(item.def_id); - tcx.sess - .struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624)) - .span_label(span, &format!("private {kind}")) - .span_label(def_span, &format!("{kind} defined here")) - .emit(); - } - tcx.check_stability(item.def_id, Some(block), span, None); - - Some(item.def_id) - } - fn qpath_to_ty( &self, span: Span, diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs new file mode 100644 index 0000000000000..191ce23efa6d1 --- /dev/null +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs @@ -0,0 +1,35 @@ +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Check that inherent associated types are dispatched on the concrete Self type. + +struct Select(T); + +impl Select { + type Projection = (); +} + +impl Select { + type Projection = bool; +} + +struct Choose(T); +struct NonCopy; + +impl Choose { + type Result = Vec; +} + +impl Choose { + type Result = (); +} + +fn main() { + let _: Select::Projection = false; + let _: Select::Projection = (); + + let _: Choose::Result = (); + let _: Choose<&str>::Result = vec!["..."]; +} diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs new file mode 100644 index 0000000000000..9b0fa8dc6f32c --- /dev/null +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs @@ -0,0 +1,39 @@ +// check-pass + +#![feature(inherent_associated_types, auto_traits, negative_impls)] +#![allow(incomplete_features)] + +use std::cmp::Ordering; + +// Check that inherent associated types are dispatched on the concrete Self type. + +struct Select(T, U); + +impl Select { + type Type = (); +} + +impl Select { + type Type = bool; +} + +impl Select { + type Type = Ordering; +} + +impl Select { + type Type = (bool, bool); +} + +fn main() { + let _: Select::Type = false; + let _: Select::Type = (true, false); + let _: Select::Type = Ordering::Equal; + let _: Select::Type = (); +} + +enum Special {} + +impl !Ordinary for Special {} + +auto trait Ordinary {} diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs new file mode 100644 index 0000000000000..7b205952f52bc --- /dev/null +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs @@ -0,0 +1,17 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Parameterized(T, U); + +impl Parameterized<(), ()> { + type Output = bool; +} + +impl Parameterized { + type Result = T; +} + +fn main() { + let _: Parameterized<(), ()>::Output = String::new(); //~ ERROR mismatched types + let _: Parameterized::Result = (); //~ ERROR mismatched types +} diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr new file mode 100644 index 0000000000000..1c77688b45ac0 --- /dev/null +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/dispatch-on-self-type-2.rs:15:44 + | +LL | let _: Parameterized<(), ()>::Output = String::new(); + | ----------------------------- ^^^^^^^^^^^^^ expected `bool`, found `String` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/dispatch-on-self-type-2.rs:16:47 + | +LL | let _: Parameterized::Result = (); + | -------------------------------- ^^ expected `bool`, found `()` + | | + | expected due to this + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr new file mode 100644 index 0000000000000..4396435a6ddc5 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr @@ -0,0 +1,16 @@ +error[E0220]: associated type `Proj` not found for `Family>` in the current scope + --> $DIR/not-found-self-type-differs.rs:17:34 + | +LL | struct Family(T); + | ---------------- associated item `Proj` not found for this struct +... +LL | type Alias = Family>::Proj; + | ^^^^ associated item not found in `Family>` + | + = note: the associated type was found for + - `Family<()>` + - `Family>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr new file mode 100644 index 0000000000000..d527db022172f --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr @@ -0,0 +1,16 @@ +error[E0220]: associated type `Proj` not found for `Family` in the current scope + --> $DIR/not-found-self-type-differs.rs:21:40 + | +LL | struct Family(T); + | ---------------- associated item `Proj` not found for this struct +... +LL | let _: Family::Proj = (); + | ^^^^ associated item not found in `Family` + | + = note: the associated type was found for + - `Family<()>` + - `Family>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.rs b/tests/ui/associated-inherent-types/not-found-self-type-differs.rs new file mode 100644 index 0000000000000..93f58dcb6e61a --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.rs @@ -0,0 +1,22 @@ +// revisions: local alias + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Family(T); + +impl Family<()> { + type Proj = (); +} + +impl Family> { + type Proj = Self; +} + +#[cfg(alias)] +type Alias = Family>::Proj; //[alias]~ ERROR associated type `Proj` not found for `Family>` + +fn main() { + #[cfg(local)] + let _: Family::Proj = (); //[local]~ ERROR associated type `Proj` not found for `Family` +} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs new file mode 100644 index 0000000000000..b00830fa1c15c --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs @@ -0,0 +1,21 @@ +// Regression test for issue #104251. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Container(T); + +impl Container { + type Yield = i32; +} + +struct Duple(T, U); + +impl Duple { + type Combination = (T, U); +} + +fn main() { + let _: Container<[u8]>::Yield = 1; //~ ERROR the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied + let _: Duple>::Combination; //~ ERROR the associated type `Combination` exists for `Duple>`, but its trait bounds were not satisfied +} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr new file mode 100644 index 0000000000000..8b13b6852374f --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr @@ -0,0 +1,27 @@ +error: the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied + --> $DIR/not-found-unsatisfied-bounds.rs:19:29 + | +LL | struct Container(T); + | --------------------------- associated item `Yield` not found for this struct +... +LL | let _: Container<[u8]>::Yield = 1; + | ^^^^^ associated type cannot be referenced on `Container<[u8]>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `[u8]: Sized` + +error: the associated type `Combination` exists for `Duple>`, but its trait bounds were not satisfied + --> $DIR/not-found-unsatisfied-bounds.rs:20:45 + | +LL | struct Duple(T, U); + | ------------------ associated item `Combination` not found for this struct +... +LL | let _: Duple>::Combination; + | ^^^^^^^^^^^ associated type cannot be referenced on `Duple>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Rc: Send` + `String: Copy` + +error: aborting due to 2 previous errors + From aa7edf70739528746d55915b96c63bd3ec43f3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 18:41:24 +0100 Subject: [PATCH 02/11] Use the correct ParamEnv --- compiler/rustc_hir_analysis/src/astconv/mod.rs | 2 +- .../dispatch-on-self-type-0.rs | 6 ++++++ ...ds.rs => not-found-unsatisfied-bounds-0.rs} | 0 ...r => not-found-unsatisfied-bounds-0.stderr} | 4 ++-- .../not-found-unsatisfied-bounds-1.rs | 18 ++++++++++++++++++ .../not-found-unsatisfied-bounds-1.stderr | 14 ++++++++++++++ 6 files changed, 41 insertions(+), 3 deletions(-) rename tests/ui/associated-inherent-types/{not-found-unsatisfied-bounds.rs => not-found-unsatisfied-bounds-0.rs} (100%) rename tests/ui/associated-inherent-types/{not-found-unsatisfied-bounds.stderr => not-found-unsatisfied-bounds-0.stderr} (91%) create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 92bff68cdbce3..f0dd4a5d5ace2 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2219,12 +2219,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Ok(None); } + let param_env = tcx.param_env(block.owner.to_def_id()); let cause = ObligationCause::misc(span, block.owner.def_id); let mut unsatisfied_predicates = Vec::new(); for &(impl_, (assoc_item, def_scope)) in &candidates { let infcx = tcx.infer_ctxt().ignoring_regions().build(); - let param_env = tcx.param_env(impl_); let impl_ty = tcx.type_of(impl_); let impl_substs = self.fresh_item_substs(impl_, &infcx); diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs index 191ce23efa6d1..f10386bd9f964 100644 --- a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs @@ -33,3 +33,9 @@ fn main() { let _: Choose::Result = (); let _: Choose<&str>::Result = vec!["..."]; } + +// Test if we use the correct `ParamEnv` when proving obligations. + +pub fn parameterized(x: T) { + let _: Choose::Result = vec![x]; +} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.rs similarity index 100% rename from tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.rs rename to tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.rs diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr similarity index 91% rename from tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr rename to tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr index 8b13b6852374f..736579067615a 100644 --- a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds.stderr +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr @@ -1,5 +1,5 @@ error: the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied - --> $DIR/not-found-unsatisfied-bounds.rs:19:29 + --> $DIR/not-found-unsatisfied-bounds-0.rs:19:29 | LL | struct Container(T); | --------------------------- associated item `Yield` not found for this struct @@ -11,7 +11,7 @@ LL | let _: Container<[u8]>::Yield = 1; `[u8]: Sized` error: the associated type `Combination` exists for `Duple>`, but its trait bounds were not satisfied - --> $DIR/not-found-unsatisfied-bounds.rs:20:45 + --> $DIR/not-found-unsatisfied-bounds-0.rs:20:45 | LL | struct Duple(T, U); | ------------------ associated item `Combination` not found for this struct diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs new file mode 100644 index 0000000000000..c80b1364ae3b8 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs @@ -0,0 +1,18 @@ +// fail-check + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Test if we use the correct `ParamEnv` when proving obligations. + +fn parameterized() { + let _: Container::Proj = String::new(); //~ ERROR the associated type `Proj` exists for `Container`, but its trait bounds were not satisfied +} + +struct Container(T); + +impl Container { + type Proj = String; +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr new file mode 100644 index 0000000000000..230bfa538b4f3 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr @@ -0,0 +1,14 @@ +error: the associated type `Proj` exists for `Container`, but its trait bounds were not satisfied + --> $DIR/not-found-unsatisfied-bounds-1.rs:9:26 + | +LL | let _: Container::Proj = String::new(); + | ^^^^ associated type cannot be referenced on `Container` due to unsatisfied trait bounds +... +LL | struct Container(T); + | ------------------- associated item `Proj` not found for this struct + | + = note: the following trait bounds were not satisfied: + `T: Clone` + +error: aborting due to previous error + From cc65ebd0d29a79a2a1d3fd77e679c763837b33c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 18:42:08 +0100 Subject: [PATCH 03/11] Make use of ObligationCtxt --- .../rustc_hir_analysis/src/astconv/errors.rs | 14 ++-- .../rustc_hir_analysis/src/astconv/mod.rs | 73 +++++++------------ 2 files changed, 33 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 191f4f0f910ad..0e9f5fb3dae65 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::traits::FulfillmentError; use rustc_middle::ty::{self, Ty}; use rustc_session::parse::feature_err; use rustc_span::lev_distance::find_best_match_for_name; @@ -226,8 +227,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, name: Ident, self_ty: Ty<'tcx>, - candidates: &[DefId], - unsatisfied_predicates: Vec>, + candidates: Vec<(DefId, (DefId, DefId))>, + fulfillment_errors: Vec>, span: Span, ) -> ErrorGuaranteed { let tcx = self.tcx(); @@ -245,16 +246,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; - if unsatisfied_predicates.is_empty() { + if fulfillment_errors.is_empty() { // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. let limit = if candidates.len() == 5 { 5 } else { 4 }; let type_candidates = candidates .iter() .take(limit) - .map(|candidate| { - format!("- `{}`", tcx.at(span).type_of(candidate).subst_identity()) - }) + .map(|&(impl_, _)| format!("- `{}`", tcx.at(span).type_of(impl_).subst_identity())) .collect::>() .join("\n"); let additional_types = if candidates.len() > limit { @@ -348,8 +347,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds. // I would do the same here if it didn't mean more code duplication. - let mut bounds: Vec<_> = unsatisfied_predicates + let mut bounds: Vec<_> = fulfillment_errors .into_iter() + .map(|error| error.root_obligation.predicate) .filter_map(format_pred) .map(|(p, _)| format!("`{}`", p)) .collect(); diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index f0dd4a5d5ace2..d3468f03eedc5 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -28,7 +28,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability::AllowUnstable; @@ -42,13 +42,11 @@ use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi; -use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::{ report_object_safety_error, suggestions::NextTypeParamName, }; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; -use rustc_trait_selection::traits::{astconv_object_safety_violations, NormalizeExt}; +use rustc_trait_selection::traits::{self, astconv_object_safety_violations, ObligationCtxt}; use smallvec::{smallvec, SmallVec}; use std::collections::BTreeSet; @@ -1948,7 +1946,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Res::Err }; - // Check if we have an enum variant. + // Check if we have an enum variant or an inherent associated type. let mut variant_resolution = None; if let Some(adt_def) = self.probe_adt(span, qself_ty) { if adt_def.is_enum() { @@ -2221,62 +2219,37 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let param_env = tcx.param_env(block.owner.to_def_id()); let cause = ObligationCause::misc(span, block.owner.def_id); - let mut unsatisfied_predicates = Vec::new(); + let mut fulfillment_errors = Vec::new(); for &(impl_, (assoc_item, def_scope)) in &candidates { let infcx = tcx.infer_ctxt().ignoring_regions().build(); + let ocx = ObligationCtxt::new(&infcx); let impl_ty = tcx.type_of(impl_); let impl_substs = self.fresh_item_substs(impl_, &infcx); let impl_ty = impl_ty.subst(tcx, impl_substs); - - let InferOk { value: impl_ty, obligations } = - infcx.at(&cause, param_env).normalize(impl_ty); + let impl_ty = ocx.normalize(&cause, param_env, impl_ty); // Check that the Self-types can be related. - let Ok(InferOk { obligations: sub_obligations, value: () }) = infcx - .at(&ObligationCause::dummy(), param_env) - .define_opaque_types(false) - .sup(impl_ty, self_ty) - else { + // FIXME(fmease): Should we use `eq` here? + if ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() { continue; - }; + } // Check whether the impl imposes obligations we have to worry about. let impl_bounds = tcx.predicates_of(impl_); let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); - let InferOk { value: impl_bounds, obligations: norm_obligations } = - infcx.at(&cause, param_env).normalize(impl_bounds); + let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); let impl_obligations = traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); - let candidate_obligations = impl_obligations - .chain(norm_obligations.into_iter()) - .chain(obligations.iter().cloned()); - - let mut matches = true; - - // Evaluate those obligations to see if they might possibly hold. - for o in candidate_obligations { - let o = infcx.resolve_vars_if_possible(o); - if !infcx.predicate_may_hold(&o) { - matches = false; - unsatisfied_predicates.push(o.predicate); - } - } + ocx.register_obligations(impl_obligations); - // Evaluate those obligations to see if they might possibly hold. - for o in sub_obligations { - let o = infcx.resolve_vars_if_possible(o); - if !infcx.predicate_may_hold(&o) { - matches = false; - unsatisfied_predicates.push(o.predicate); - } - } - - if !matches { + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + fulfillment_errors = errors; continue; } @@ -2286,19 +2259,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { bug!("unreachable: `lookup_inherent_assoc_ty` is only called on ADTs"); }; - let item_substs = - self.create_substs_for_associated_item(span, assoc_item, segment, adt_substs); - // FIXME(inherent_associated_types): Check if the obligations arising from the - // where-clause & the bounds on the associated type and its parameters hold. + let item_substs = self.create_substs_for_associated_item( + span, assoc_item, segment, + // FIXME(fmease, #107468, #105305): Don't use `adt_substs` here but `impl_substs`. + adt_substs, + ); + + // FIXME(fmease, #106722): Check if the bounds on the parameters of the + // associated type hold, if any. let ty = tcx.type_of(assoc_item).subst(tcx, item_substs); + + // FIXME(fmease): Don't return early here! There might be multiple applicable candidates. return Ok(Some((ty, assoc_item))); } Err(self.complain_about_inherent_assoc_type_not_found( name, self_ty, - &candidates.into_iter().map(|(impl_, _)| impl_).collect::>(), - unsatisfied_predicates, + candidates, + fulfillment_errors, span, )) } From b5e73bfe90f7b4905829f1a6509b61ac5577ee07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 17:06:27 +0100 Subject: [PATCH 04/11] Groundwork for detecting ambiguous candidates NB: Since we are using the same InferCtxt in each iteration, we essentially *spoil* the inference variables and we only ever get at most *one* applicable candidate (only the 1st candidate has clean variables that can still unify correctly). --- .../rustc_hir_analysis/src/astconv/errors.rs | 60 +++++++++++++++++++ .../rustc_hir_analysis/src/astconv/mod.rs | 27 ++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 0e9f5fb3dae65..f3a03805a4469 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -222,6 +222,66 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.emit() } + pub(crate) fn complain_about_ambiguous_inherent_assoc_type( + &self, + name: Ident, + candidates: Vec<(DefId, DefId)>, + span: Span, + ) -> ErrorGuaranteed { + let mut err = struct_span_err!( + self.tcx().sess, + name.span, + E0034, + "multiple applicable items in scope" + ); + err.span_label(name.span, format!("multiple `{name}` found")); + self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span); + err.emit() + } + + // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. + fn note_ambiguous_inherent_assoc_type( + &self, + err: &mut Diagnostic, + candidates: Vec<(DefId, DefId)>, + span: Span, + ) { + let tcx = self.tcx(); + + // Dynamic limit to avoid hiding just one candidate, which is silly. + let limit = if candidates.len() == 5 { 5 } else { 4 }; + + for (index, &(assoc_item, _)) in candidates.iter().take(limit).enumerate() { + let impl_ = tcx.impl_of_method(assoc_item).unwrap(); + + let note_span = if assoc_item.is_local() { + Some(tcx.def_span(assoc_item)) + } else if impl_.is_local() { + Some(tcx.def_span(impl_)) + } else { + None + }; + + let title = if candidates.len() > 1 { + format!("candidate #{}", index + 1) + } else { + "the candidate".into() + }; + + let impl_ty = tcx.at(span).type_of(impl_).subst_identity(); + let note = format!("{title} is defined in an impl for the type `{impl_ty}`"); + + if let Some(span) = note_span { + err.span_note(span, ¬e); + } else { + err.note(¬e); + } + } + if candidates.len() > limit { + err.note(&format!("and {} others", candidates.len() - limit)); + } + } + // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. pub(crate) fn complain_about_inherent_assoc_type_not_found( &self, diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index d3468f03eedc5..43db8af7bac81 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2217,12 +2217,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Ok(None); } + // In contexts that have no inference context, just make a new one. + // We do need a local variable to store it, though. + let infcx_; + let infcx = match self.infcx() { + Some(infcx) => infcx, + None => { + assert!(!self_ty.needs_infer()); + infcx_ = tcx.infer_ctxt().ignoring_regions().build(); + &infcx_ + } + }; + let param_env = tcx.param_env(block.owner.to_def_id()); let cause = ObligationCause::misc(span, block.owner.def_id); let mut fulfillment_errors = Vec::new(); + let mut applicable_candidates = Vec::new(); for &(impl_, (assoc_item, def_scope)) in &candidates { - let infcx = tcx.infer_ctxt().ignoring_regions().build(); let ocx = ObligationCtxt::new(&infcx); let impl_ty = tcx.type_of(impl_); @@ -2253,6 +2265,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { continue; } + applicable_candidates.push((assoc_item, def_scope)); + } + + if applicable_candidates.len() > 1 { + return Err(self.complain_about_ambiguous_inherent_assoc_type( + name, + applicable_candidates, + span, + )); + } + + if let Some((assoc_item, def_scope)) = applicable_candidates.pop() { self.check_assoc_ty(assoc_item, name, def_scope, block, span); let ty::Adt(_, adt_substs) = self_ty.kind() else { @@ -2269,7 +2293,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // associated type hold, if any. let ty = tcx.type_of(assoc_item).subst(tcx, item_substs); - // FIXME(fmease): Don't return early here! There might be multiple applicable candidates. return Ok(Some((ty, assoc_item))); } From 3dc38fbc9115f0e8cf61848938e05e1964ee29bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 16:55:07 +0100 Subject: [PATCH 05/11] Switch from for-loop to filter_map --- .../rustc_hir_analysis/src/astconv/mod.rs | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 43db8af7bac81..c8ff609dea555 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2232,41 +2232,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let param_env = tcx.param_env(block.owner.to_def_id()); let cause = ObligationCause::misc(span, block.owner.def_id); let mut fulfillment_errors = Vec::new(); - let mut applicable_candidates = Vec::new(); - - for &(impl_, (assoc_item, def_scope)) in &candidates { - let ocx = ObligationCtxt::new(&infcx); + let mut applicable_candidates: Vec<_> = candidates + .iter() + .filter_map(|&(impl_, (assoc_item, def_scope))| { + let ocx = ObligationCtxt::new(&infcx); - let impl_ty = tcx.type_of(impl_); - let impl_substs = self.fresh_item_substs(impl_, &infcx); - let impl_ty = impl_ty.subst(tcx, impl_substs); - let impl_ty = ocx.normalize(&cause, param_env, impl_ty); + let impl_ty = tcx.type_of(impl_); + let impl_substs = self.fresh_item_substs(impl_, &infcx); + let impl_ty = impl_ty.subst(tcx, impl_substs); + let impl_ty = ocx.normalize(&cause, param_env, impl_ty); - // Check that the Self-types can be related. - // FIXME(fmease): Should we use `eq` here? - if ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() { - continue; - } + // Check that the Self-types can be related. + // FIXME(fmease): Should we use `eq` here? + ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?; - // Check whether the impl imposes obligations we have to worry about. - let impl_bounds = tcx.predicates_of(impl_); - let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); + // Check whether the impl imposes obligations we have to worry about. + let impl_bounds = tcx.predicates_of(impl_); + let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); - let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); + let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); - let impl_obligations = - traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); + let impl_obligations = + traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); - ocx.register_obligations(impl_obligations); + ocx.register_obligations(impl_obligations); - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - fulfillment_errors = errors; - continue; - } + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + fulfillment_errors = errors; + return None; + } - applicable_candidates.push((assoc_item, def_scope)); - } + Some((assoc_item, def_scope)) + }) + .collect(); if applicable_candidates.len() > 1 { return Err(self.complain_about_ambiguous_inherent_assoc_type( From 6065867a7e4379a12e495912a41318f871104270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 16:58:18 +0100 Subject: [PATCH 06/11] Use InferCtxt::probe to properly detect ambiguous candidates --- .../rustc_hir_analysis/src/astconv/mod.rs | 47 ++++++++++--------- .../ui/associated-inherent-types/ambiguity.rs | 16 +++++++ .../ambiguity.stderr | 20 ++++++++ 3 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 tests/ui/associated-inherent-types/ambiguity.rs create mode 100644 tests/ui/associated-inherent-types/ambiguity.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index c8ff609dea555..c22ebc1c65993 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2235,35 +2235,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut applicable_candidates: Vec<_> = candidates .iter() .filter_map(|&(impl_, (assoc_item, def_scope))| { - let ocx = ObligationCtxt::new(&infcx); + infcx.probe(|_| { + let ocx = ObligationCtxt::new_in_snapshot(&infcx); - let impl_ty = tcx.type_of(impl_); - let impl_substs = self.fresh_item_substs(impl_, &infcx); - let impl_ty = impl_ty.subst(tcx, impl_substs); - let impl_ty = ocx.normalize(&cause, param_env, impl_ty); + let impl_ty = tcx.type_of(impl_); + let impl_substs = self.fresh_item_substs(impl_, &infcx); + let impl_ty = impl_ty.subst(tcx, impl_substs); + let impl_ty = ocx.normalize(&cause, param_env, impl_ty); - // Check that the Self-types can be related. - // FIXME(fmease): Should we use `eq` here? - ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?; + // Check that the Self-types can be related. + // FIXME(fmease): Should we use `eq` here? + ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?; - // Check whether the impl imposes obligations we have to worry about. - let impl_bounds = tcx.predicates_of(impl_); - let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); + // Check whether the impl imposes obligations we have to worry about. + let impl_bounds = tcx.predicates_of(impl_); + let impl_bounds = impl_bounds.instantiate(tcx, impl_substs); - let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); + let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); - let impl_obligations = - traits::predicates_for_generics(|_, _| cause.clone(), param_env, impl_bounds); + let impl_obligations = traits::predicates_for_generics( + |_, _| cause.clone(), + param_env, + impl_bounds, + ); - ocx.register_obligations(impl_obligations); + ocx.register_obligations(impl_obligations); - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - fulfillment_errors = errors; - return None; - } + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + fulfillment_errors = errors; + return None; + } - Some((assoc_item, def_scope)) + Some((assoc_item, def_scope)) + }) }) .collect(); diff --git a/tests/ui/associated-inherent-types/ambiguity.rs b/tests/ui/associated-inherent-types/ambiguity.rs new file mode 100644 index 0000000000000..73920555b3e04 --- /dev/null +++ b/tests/ui/associated-inherent-types/ambiguity.rs @@ -0,0 +1,16 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Wrapper(T); + +impl Wrapper { + type Foo = i32; +} + +impl Wrapper<()> { + type Foo = (); +} + +fn main() { + let _: Wrapper<_>::Foo = (); //~ ERROR multiple applicable items in scope +} diff --git a/tests/ui/associated-inherent-types/ambiguity.stderr b/tests/ui/associated-inherent-types/ambiguity.stderr new file mode 100644 index 0000000000000..155c296cbb3c4 --- /dev/null +++ b/tests/ui/associated-inherent-types/ambiguity.stderr @@ -0,0 +1,20 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/ambiguity.rs:15:24 + | +LL | let _: Wrapper<_>::Foo = (); + | ^^^ multiple `Foo` found + | +note: candidate #1 is defined in an impl for the type `Wrapper` + --> $DIR/ambiguity.rs:7:5 + | +LL | type Foo = i32; + | ^^^^^^^^ +note: candidate #2 is defined in an impl for the type `Wrapper<()>` + --> $DIR/ambiguity.rs:11:5 + | +LL | type Foo = (); + | ^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0034`. From 77ea90ec71926df7d478834d34d1fefce40cc456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 17 Feb 2023 18:42:47 +0100 Subject: [PATCH 07/11] Fix substitution bug --- .../rustc_hir_analysis/src/astconv/errors.rs | 12 +++++----- .../rustc_hir_analysis/src/astconv/mod.rs | 19 +++++++-------- .../dispatch-on-self-type-0.rs | 2 +- .../dispatch-on-self-type-2.stderr | 2 +- .../substitute-params-bad.rs | 23 +++++++++++++++++++ .../substitute-params-bad.stderr | 20 ++++++++++++++++ ...truct-generics.rs => substitute-params.rs} | 8 +++++++ 7 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 tests/ui/associated-inherent-types/substitute-params-bad.rs create mode 100644 tests/ui/associated-inherent-types/substitute-params-bad.stderr rename tests/ui/associated-inherent-types/{struct-generics.rs => substitute-params.rs} (55%) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index f3a03805a4469..b039e654fd006 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -225,7 +225,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub(crate) fn complain_about_ambiguous_inherent_assoc_type( &self, name: Ident, - candidates: Vec<(DefId, DefId)>, + candidates: Vec, span: Span, ) -> ErrorGuaranteed { let mut err = struct_span_err!( @@ -243,7 +243,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn note_ambiguous_inherent_assoc_type( &self, err: &mut Diagnostic, - candidates: Vec<(DefId, DefId)>, + candidates: Vec, span: Span, ) { let tcx = self.tcx(); @@ -251,11 +251,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Dynamic limit to avoid hiding just one candidate, which is silly. let limit = if candidates.len() == 5 { 5 } else { 4 }; - for (index, &(assoc_item, _)) in candidates.iter().take(limit).enumerate() { - let impl_ = tcx.impl_of_method(assoc_item).unwrap(); + for (index, &item) in candidates.iter().take(limit).enumerate() { + let impl_ = tcx.impl_of_method(item).unwrap(); - let note_span = if assoc_item.is_local() { - Some(tcx.def_span(assoc_item)) + let note_span = if item.is_local() { + Some(tcx.def_span(item)) } else if impl_.is_local() { Some(tcx.def_span(impl_)) } else { diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index c22ebc1c65993..f43b92254eb03 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2267,7 +2267,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return None; } - Some((assoc_item, def_scope)) + // FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot. + Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs))) }) }) .collect(); @@ -2275,23 +2276,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if applicable_candidates.len() > 1 { return Err(self.complain_about_ambiguous_inherent_assoc_type( name, - applicable_candidates, + applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(), span, )); } - if let Some((assoc_item, def_scope)) = applicable_candidates.pop() { + if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() { self.check_assoc_ty(assoc_item, name, def_scope, block, span); - let ty::Adt(_, adt_substs) = self_ty.kind() else { - bug!("unreachable: `lookup_inherent_assoc_ty` is only called on ADTs"); - }; + // FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, + // we still need to register region obligations for regionck to prove/disprove. - let item_substs = self.create_substs_for_associated_item( - span, assoc_item, segment, - // FIXME(fmease, #107468, #105305): Don't use `adt_substs` here but `impl_substs`. - adt_substs, - ); + let item_substs = + self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs); // FIXME(fmease, #106722): Check if the bounds on the parameters of the // associated type hold, if any. diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs index f10386bd9f964..f846bfa4168ac 100644 --- a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs @@ -31,7 +31,7 @@ fn main() { let _: Select::Projection = (); let _: Choose::Result = (); - let _: Choose<&str>::Result = vec!["..."]; + let _: Choose::Result = vec![true]; } // Test if we use the correct `ParamEnv` when proving obligations. diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr index 1c77688b45ac0..c9a48872af40e 100644 --- a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr +++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr @@ -10,7 +10,7 @@ error[E0308]: mismatched types --> $DIR/dispatch-on-self-type-2.rs:16:47 | LL | let _: Parameterized::Result = (); - | -------------------------------- ^^ expected `bool`, found `()` + | -------------------------------- ^^ expected `u32`, found `()` | | | expected due to this diff --git a/tests/ui/associated-inherent-types/substitute-params-bad.rs b/tests/ui/associated-inherent-types/substitute-params-bad.rs new file mode 100644 index 0000000000000..00eb1a14da47a --- /dev/null +++ b/tests/ui/associated-inherent-types/substitute-params-bad.rs @@ -0,0 +1,23 @@ +// Regression test for issue #105305 and for +// https://github.com/rust-lang/rust/issues/107468#issuecomment-1409096700 + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S { //~ ERROR lifetime parameters must be declared prior to type and const parameters + type P = T; +} + +struct Subj(T); + +impl Subj<(T, S)> { + type Un = (T, S); +} + +fn main() { + type A = S<()>::P; + + let _: Subj<(i32, i32)>::Un = 0i32; //~ ERROR mismatched types +} diff --git a/tests/ui/associated-inherent-types/substitute-params-bad.stderr b/tests/ui/associated-inherent-types/substitute-params-bad.stderr new file mode 100644 index 0000000000000..7a7808ba67b14 --- /dev/null +++ b/tests/ui/associated-inherent-types/substitute-params-bad.stderr @@ -0,0 +1,20 @@ +error: lifetime parameters must be declared prior to type and const parameters + --> $DIR/substitute-params-bad.rs:9:9 + | +LL | impl S { + | ----^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, T>` + +error[E0308]: mismatched types + --> $DIR/substitute-params-bad.rs:22:35 + | +LL | let _: Subj<(i32, i32)>::Un = 0i32; + | -------------------- ^^^^ expected `(i32, i32)`, found `i32` + | | + | expected due to this + | + = note: expected tuple `(i32, i32)` + found type `i32` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/struct-generics.rs b/tests/ui/associated-inherent-types/substitute-params.rs similarity index 55% rename from tests/ui/associated-inherent-types/struct-generics.rs rename to tests/ui/associated-inherent-types/substitute-params.rs index 8952b37917308..e94d683315903 100644 --- a/tests/ui/associated-inherent-types/struct-generics.rs +++ b/tests/ui/associated-inherent-types/substitute-params.rs @@ -9,7 +9,15 @@ impl S { type P = T; } +impl S<(T,)> { + type Un = T; +} + fn main() { + // Regression test for issue #104240. type A = S<()>::P; let _: A = (); + + // Regression test for issue #107468. + let _: S<(i32,)>::Un = 0i32; } From 6eb6455c46454fc09fe892d3f9193477a323802c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 1 Feb 2023 17:20:55 +0100 Subject: [PATCH 08/11] Add a test and several known bugs --- .../bugs/ice-substitution.rs | 23 ++++++++++++++ .../bugs/ice-substitution.stderr | 6 ++++ .../bugs/inference-fail.rs | 15 +++++++++ .../bugs/inference-fail.stderr | 9 ++++++ .../bugs/lack-of-regionck.rs | 19 ++++++++++++ ...-self-type-differs-shadowing-trait-item.rs | 31 +++++++++++++++++++ ...ffers-shadowing-trait-item.shadowed.stderr | 15 +++++++++ ...fers-shadowing-trait-item.uncovered.stderr | 9 ++++++ 8 files changed, 127 insertions(+) create mode 100644 tests/ui/associated-inherent-types/bugs/ice-substitution.rs create mode 100644 tests/ui/associated-inherent-types/bugs/ice-substitution.stderr create mode 100644 tests/ui/associated-inherent-types/bugs/inference-fail.rs create mode 100644 tests/ui/associated-inherent-types/bugs/inference-fail.stderr create mode 100644 tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs new file mode 100644 index 0000000000000..53ac79e0561ba --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs @@ -0,0 +1,23 @@ +// known-bug: unknown +// failure-status: 101 +// normalize-stderr-test "note: .*\n\n" -> "" +// normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" +// rustc-env:RUST_BACKTRACE=0 + +// FIXME: I presume a type variable that couldn't be solved by `resolve_vars_if_possible` +// escapes the InferCtxt snapshot. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Cont(T); + +impl Cont { + type Out = Vec; +} + +pub fn weird(x: T) { + let _: Cont<_>::Out = vec![true]; +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr b/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr new file mode 100644 index 0000000000000..7b0d1c5051624 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr @@ -0,0 +1,6 @@ +error: the compiler unexpectedly panicked. this is a bug. + +query stack during panic: +#0 [typeck] type-checking `weird` +#1 [typeck_item_bodies] type-checking all item bodies +end of query stack diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.rs b/tests/ui/associated-inherent-types/bugs/inference-fail.rs new file mode 100644 index 0000000000000..a920b412b1a49 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/inference-fail.rs @@ -0,0 +1,15 @@ +// known-bug: unknown + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S<()> { + type P = i128; +} + +fn main() { + // We fail to infer `_ == ()` here. + let _: S<_>::P; +} diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.stderr b/tests/ui/associated-inherent-types/bugs/inference-fail.stderr new file mode 100644 index 0000000000000..425691bd6c4f6 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/inference-fail.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/inference-fail.rs:14:14 + | +LL | let _: S<_>::P; + | ^ cannot infer type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs new file mode 100644 index 0000000000000..632dbf3854b2b --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs @@ -0,0 +1,19 @@ +// known-bug: unknown +// check-pass + +// We currently don't region-check inherent associated type projections at all. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features, dead_code)] + +struct S(T); + +impl S<&'static ()> { + type T = (); +} + +fn usr<'a>() { + let _: S::<&'a ()>::T; // this should *fail* but it doesn't! +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs new file mode 100644 index 0000000000000..d2efb24c66620 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs @@ -0,0 +1,31 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Check that it's okay to report “[inherent] associated type […] not found” for inherent associated +// type candidates that are not applicable (due to unsuitable Self type) even if there exists a +// “shadowed” associated type from a trait with the same name since its use would be ambiguous +// anyway if the IAT didn't exist. +// FIXME(inherent_associated_types): Figure out which error would be more helpful here. + +// revisions: shadowed uncovered + +struct S(T); + +trait Tr { + type Pr; +} + +impl Tr for S { + type Pr = (); +} + +#[cfg(shadowed)] +impl S<()> { + type Pr = i32; +} + +fn main() { + let _: S::::Pr = (); + //[shadowed]~^ ERROR associated type `Pr` not found + //[uncovered]~^^ ERROR ambiguous associated type +} diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr new file mode 100644 index 0000000000000..3561db354c03f --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr @@ -0,0 +1,15 @@ +error[E0220]: associated type `Pr` not found for `S` in the current scope + --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:23 + | +LL | struct S(T); + | ----------- associated item `Pr` not found for this struct +... +LL | let _: S::::Pr = (); + | ^^ associated item not found in `S` + | + = note: the associated type was found for + - `S<()>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr new file mode 100644 index 0000000000000..88c72042ce2d0 --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr @@ -0,0 +1,9 @@ +error[E0223]: ambiguous associated type + --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:12 + | +LL | let _: S::::Pr = (); + | ^^^^^^^^^^^^^ help: use the fully-qualified path: ` as Tr>::Pr` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0223`. From 569ca2bad04dec9c6b7bc4864bd0216e3bea9c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 19 Feb 2023 17:21:29 +0100 Subject: [PATCH 09/11] Deduplicate fresh_item_substs --- .../rustc_hir_analysis/src/astconv/mod.rs | 65 ++++++++++--------- compiler/rustc_hir_typeck/src/method/probe.rs | 30 +-------- 2 files changed, 35 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index f43b92254eb03..4098de59f9e98 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2239,7 +2239,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let ocx = ObligationCtxt::new_in_snapshot(&infcx); let impl_ty = tcx.type_of(impl_); - let impl_substs = self.fresh_item_substs(impl_, &infcx); + let impl_substs = infcx.fresh_item_substs(impl_); let impl_ty = impl_ty.subst(tcx, impl_substs); let impl_ty = ocx.normalize(&cause, param_env, impl_ty); @@ -2306,36 +2306,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { )) } - // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. - fn fresh_item_substs(&self, def_id: DefId, infcx: &InferCtxt<'tcx>) -> SubstsRef<'tcx> { - let tcx = self.tcx(); - - InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => infcx - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - infcx - .next_const_var( - tcx.type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - origin, - ) - .into() - } - }) - } - fn lookup_assoc_ty( &self, name: Ident, @@ -3531,3 +3501,36 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } } + +pub trait InferCtxtExt<'tcx> { + fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx>; +} + +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { + fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { + InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } => self + .next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::SubstitutionPlaceholder, + span: self.tcx.def_span(def_id), + }) + .into(), + GenericParamDefKind::Const { .. } => { + let span = self.tcx.def_span(def_id); + let origin = ConstVariableOrigin { + kind: ConstVariableOriginKind::SubstitutionPlaceholder, + span, + }; + self.next_const_var( + self.tcx + .type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + origin, + ) + .into() + } + }) + } +} diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 0b9226802cf5e..eb6c0b7686b6d 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,12 +9,11 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir_analysis::astconv::InferCtxtExt as _; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::AssocItem; @@ -1941,33 +1940,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) } - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: self.tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = self.tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - self.next_const_var( - self.tcx - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - origin, - ) - .into() - } - }) - } - /// Replaces late-bound-regions bound by `value` with `'static` using /// `ty::erase_late_bound_regions`. /// From 00b976a1381b301a74b4f97f041de9a6cc58dba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 19 Feb 2023 18:28:44 +0100 Subject: [PATCH 10/11] Collect fulfillment errors across impls --- .../rustc_hir_analysis/src/astconv/mod.rs | 4 ++-- ...nd-unsatisfied-bounds-in-multiple-impls.rs | 20 +++++++++++++++++++ ...nsatisfied-bounds-in-multiple-impls.stderr | 20 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs create mode 100644 tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 4098de59f9e98..03b1af76f33b9 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2261,9 +2261,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ocx.register_obligations(impl_obligations); - let errors = ocx.select_where_possible(); + let mut errors = ocx.select_where_possible(); if !errors.is_empty() { - fulfillment_errors = errors; + fulfillment_errors.append(&mut errors); return None; } diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs new file mode 100644 index 0000000000000..5b0e8de9c580b --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs @@ -0,0 +1,20 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(A, B); +struct Featureless; + +trait One {} +trait Two {} + +impl S { + type X = (); +} + +impl S { + type X = String; +} + +fn main() { + let _: S::::X; //~ ERROR the associated type `X` exists for `S`, but its trait bounds were not satisfied +} diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr new file mode 100644 index 0000000000000..3ddab25deb54c --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr @@ -0,0 +1,20 @@ +error: the associated type `X` exists for `S`, but its trait bounds were not satisfied + --> $DIR/not-found-unsatisfied-bounds-in-multiple-impls.rs:19:43 + | +LL | struct S(A, B); + | -------------- associated item `X` not found for this struct +LL | struct Featureless; + | ------------------ + | | + | doesn't satisfy `Featureless: One` + | doesn't satisfy `Featureless: Two` +... +LL | let _: S::::X; + | ^ associated type cannot be referenced on `S` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Featureless: One` + `Featureless: Two` + +error: aborting due to previous error + From f2253dad24ad541618c939f363688d4e90fca72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 19 Feb 2023 22:54:47 +0100 Subject: [PATCH 11/11] Add some FIXMEs for follow-up PRs --- compiler/rustc_hir_analysis/src/astconv/errors.rs | 8 ++++++-- compiler/rustc_hir_analysis/src/astconv/mod.rs | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index b039e654fd006..a9c2886b41448 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -291,6 +291,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fulfillment_errors: Vec>, span: Span, ) -> ErrorGuaranteed { + // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`. + // Either + // * update this code by applying changes similar to #106702 or by taking a + // Vec<(DefId, (DefId, DefId), Option>>)> or + // * deduplicate this code across the two crates. + let tcx = self.tcx(); let adt_did = self_ty.ty_adt_def().map(|def| def.did()); @@ -338,7 +344,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut bound_spans = Vec::new(); - // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { let msg = format!( "doesn't satisfy `{}`", @@ -367,7 +372,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; - // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. let format_pred = |pred: ty::Predicate<'tcx>| { let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 03b1af76f33b9..716b4fc6ae36f 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2284,8 +2284,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() { self.check_assoc_ty(assoc_item, name, def_scope, block, span); - // FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, - // we still need to register region obligations for regionck to prove/disprove. + // FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, we still + // need to relate the Self-type with fresh item substs & register region obligations for + // regionck to prove/disprove. let item_substs = self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs);