From 497356779a2dbca8a61308c933550718bc96726d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Aug 2023 04:11:03 +0000 Subject: [PATCH] Normalize yeet --- compiler/rustc_hir_typeck/src/expr.rs | 7 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 89 ++++++++++++------- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 44 ++++----- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 12 --- .../rustc_hir_typeck/src/gather_locals.rs | 8 +- compiler/rustc_hir_typeck/src/lib.rs | 1 - .../rustc_hir_typeck/src/method/confirm.rs | 2 +- compiler/rustc_hir_typeck/src/pat.rs | 4 +- .../new-solver/normalize-path-for-method.rs | 18 ++++ 9 files changed, 109 insertions(+), 76 deletions(-) create mode 100644 tests/ui/traits/new-solver/normalize-path-for-method.rs diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 8f5737dd4ad1d..6454a02c11595 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1317,7 +1317,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find the type of `e`. Supply hints based on the type we are casting to, // if appropriate. let t_cast = self.to_ty_saving_user_provided_ty(t); - let t_cast = self.resolve_vars_if_possible(t_cast); + // FIXME(-Ztrait-solver): This structural resolve is only here for diagnostics, + // because we use the TyKind to early return from `CastCheck::new`. + let t_cast = self.try_structurally_resolve_type(t.span, t_cast); let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); let t_expr = self.resolve_vars_if_possible(t_expr); @@ -3142,7 +3144,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &[Ident], expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let container = self.to_ty(container).normalized; + // We only need to normalize in the old solver + let container = self.normalize(expr.span, self.to_ty(container)); let mut field_indices = Vec::with_capacity(fields.len()); let mut current_container = container; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index ecafb50f42094..b15133faca2ad 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -2,7 +2,7 @@ use crate::callee::{self, DeferredCallResolution}; use crate::errors::CtorIsPrivate; use crate::method::{self, MethodCallee, SelfSource}; use crate::rvalue_scopes; -use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, RawTy}; +use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey}; @@ -324,6 +324,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } + // FIXME(-Ztrait-solver=next): This could be replaced with `try_structurally_resolve` + // calls after the new trait solver is stable. + /// TODO: I don't know what to call this. + pub(super) fn structurally_normalize_after_astconv( + &self, + span: Span, + value: Ty<'tcx>, + ) -> Ty<'tcx> { + match self + .at(&self.misc(span), self.param_env) + .structurally_normalize(value, &mut **self.inh.fulfillment_cx.borrow_mut()) + { + Ok(ty) => ty, + Err(errors) => { + let guar = self.err_ctxt().report_fulfillment_errors(&errors); + Ty::new_error(self.tcx, guar) + } + } + } + pub fn require_type_meets( &self, ty: Ty<'tcx>, @@ -374,37 +394,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn handle_raw_ty(&self, span: Span, ty: Ty<'tcx>) -> RawTy<'tcx> { - RawTy { raw: ty, normalized: self.normalize(span, ty) } - } - - pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> RawTy<'tcx> { + pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { let t = self.astconv().ast_ty_to_ty(ast_t); self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); - self.handle_raw_ty(ast_t.span, t) + t } pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { let ty = self.to_ty(ast_ty); debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); - if Self::can_contain_user_lifetime_bounds(ty.raw) { - let c_ty = self.canonicalize_response(UserType::Ty(ty.raw)); + if Self::can_contain_user_lifetime_bounds(ty) { + let c_ty = self.canonicalize_response(UserType::Ty(ty)); debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); } - ty.normalized + self.normalize(ast_ty.span, ty) } - pub(super) fn user_args_for_adt(ty: RawTy<'tcx>) -> UserArgs<'tcx> { - match (ty.raw.kind(), ty.normalized.kind()) { + pub(super) fn user_args_for_adt(raw: Ty<'tcx>, normalized: Ty<'tcx>) -> UserArgs<'tcx> { + match (raw.kind(), normalized.kind()) { (ty::Adt(_, args), _) => UserArgs { args, user_self_ty: None }, (_, ty::Adt(adt, args)) => UserArgs { args, - user_self_ty: Some(UserSelfTy { impl_def_id: adt.did(), self_ty: ty.raw }), + user_self_ty: Some(UserSelfTy { impl_def_id: adt.did(), self_ty: raw }), }, - _ => bug!("non-adt type {:?}", ty), + _ => bug!("non-adt type {:?}", raw), } } @@ -817,7 +833,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &'tcx QPath<'tcx>, hir_id: hir::HirId, span: Span, - ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { + ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { debug!( "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span @@ -841,15 +857,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We manually call `register_wf_obligation` in the success path // below. let ty = self.astconv().ast_ty_to_ty_in_path(qself); - (self.handle_raw_ty(span, ty), qself, segment) + (ty, qself, segment) } QPath::LangItem(..) => { bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`") } }; + + // FIXME(-Ztrait-solver=next): Can use `try_structurally_resolve` after migration. + let normalized = + if !ty.is_ty_var() { self.structurally_normalize_after_astconv(span, ty) } else { ty }; + if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) { - self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None)); + self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); // Return directly on cache hit. This is useful to avoid doubly reporting // errors with default match binding modes. See #44614. let def = cached_result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)); @@ -857,7 +878,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let item_name = item_segment.ident; let result = self - .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) + .resolve_fully_qualified_call(span, item_name, normalized, qself.span, hir_id) .and_then(|r| { // lint bare trait if the method is found in the trait if span.edition().at_least_rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { @@ -876,14 +897,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let trait_missing_method = - matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait(); + matches!(error, method::MethodError::NoMatch(_)) && normalized.is_trait(); // If we have a path like `MyTrait::missing_method`, then don't register // a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise, // register a WF obligation so that we can detect any additional // errors in the self type. if !trait_missing_method { self.register_wf_obligation( - ty.raw.into(), + ty.into(), qself.span, traits::WellFormed(None), ); @@ -902,7 +923,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if item_name.name != kw::Empty { if let Some(mut e) = self.report_method_error( span, - ty.normalized, + normalized, item_name, SelfSource::QPath(qself), error, @@ -918,7 +939,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); if result.is_ok() { - self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None)); + self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); } // Write back the new resolution. @@ -1082,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn instantiate_value_path( &self, segments: &[hir::PathSegment<'_>], - self_ty: Option>, + self_ty: Option>, res: Res, span: Span, hir_id: hir::HirId, @@ -1093,7 +1114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Res::Local(_) | Res::SelfCtor(_) => vec![], Res::Def(kind, def_id) => self.astconv().def_ids_for_value_path_segments( segments, - self_ty.map(|ty| ty.raw), + self_ty.map(|ty| ty), kind, def_id, span, @@ -1107,8 +1128,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) if let Some(self_ty) = self_ty => { - let adt_def = self_ty.normalized.ty_adt_def().unwrap(); - user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty: self_ty.raw }); + let adt_def = self.structurally_normalize_after_astconv(span, self_ty).ty_adt_def().unwrap(); + user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty }); is_alias_variant_ctor = true; } Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { @@ -1127,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // inherent impl, we need to record the // `T` for posterity (see `UserSelfTy` for // details). - let self_ty = self_ty.expect("UFCS sugared assoc missing Self").raw; + let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty }); } } @@ -1206,9 +1227,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path_segs.last().is_some_and(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self); let (res, self_ctor_args) = if let Res::SelfCtor(impl_def_id) = res { - let ty = - self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id).instantiate_identity()); - match ty.normalized.ty_adt_def() { + let ty = tcx.at(span).type_of(impl_def_id).instantiate_identity(); + let normalized = self.structurally_normalize_after_astconv(span, ty); + match normalized.ty_adt_def() { Some(adt_def) if adt_def.has_ctor() => { let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap(); // Check the visibility of the ctor. @@ -1218,7 +1239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit_err(CtorIsPrivate { span, def: tcx.def_path_str(adt_def.did()) }); } let new_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); - let user_args = Self::user_args_for_adt(ty); + let user_args = Self::user_args_for_adt(ty, normalized); user_self_ty = user_args.user_self_ty; (new_res, Some(user_args.args)) } @@ -1227,7 +1248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, "the `Self` constructor can only be used with tuple or unit structs", ); - if let Some(adt_def) = ty.normalized.ty_adt_def() { + if let Some(adt_def) = normalized.ty_adt_def() { match adt_def.adt_kind() { AdtKind::Enum => { err.help("did you mean to use one of the enum's variants?"); @@ -1299,7 +1320,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.fcx.astconv().ast_region_to_region(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.fcx.to_ty(ty).raw.into() + self.fcx.to_ty(ty).into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { self.fcx.const_arg_to_const(&ct.value, param.def_id).into() @@ -1367,7 +1388,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_id, &[], has_self, - self_ty.map(|s| s.raw), + self_ty.map(|s| s), &arg_count, &mut CreateCtorSubstsContext { fcx: self, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index e102c51c7eeca..a22621fbf4955 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -6,7 +6,7 @@ use crate::method::MethodCallee; use crate::TupleArgumentsFlag::*; use crate::{errors, Expectation::*}; use crate::{ - struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy, TupleArgumentsFlag, + struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, TupleArgumentsFlag, }; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; @@ -1346,6 +1346,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Result<(&'tcx ty::VariantDef, Ty<'tcx>), ErrorGuaranteed> { let path_span = qpath.span(); let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); + let normalized = self.structurally_normalize_after_astconv(path_span, ty); + let variant = match def { Res::Err => { let guar = @@ -1353,18 +1355,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.set_tainted_by_errors(guar); return Err(guar); } - Res::Def(DefKind::Variant, _) => match ty.normalized.ty_adt_def() { - Some(adt) => { - Some((adt.variant_of_res(def), adt.did(), Self::user_args_for_adt(ty))) - } - _ => bug!("unexpected type: {:?}", ty.normalized), + Res::Def(DefKind::Variant, _) => match normalized.ty_adt_def() { + Some(adt) => Some(( + adt.variant_of_res(def), + adt.did(), + Self::user_args_for_adt(ty, normalized), + )), + _ => bug!("unexpected type: {:?}", normalized), }, Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfTyParam { .. } - | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() { - Some(adt) if !adt.is_enum() => { - Some((adt.non_enum_variant(), adt.did(), Self::user_args_for_adt(ty))) - } + | Res::SelfTyAlias { .. } => match normalized.ty_adt_def() { + Some(adt) if !adt.is_enum() => Some(( + adt.non_enum_variant(), + adt.did(), + Self::user_args_for_adt(ty, normalized), + )), _ => None, }, _ => bug!("unexpected definition: {:?}", def), @@ -1379,9 +1385,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check bounds on type arguments used in the path. self.add_required_obligations_for_hir(path_span, did, args, hir_id); - Ok((variant, ty.normalized)) + Ok((variant, normalized)) } else { - Err(match *ty.normalized.kind() { + Err(match *normalized.kind() { ty::Error(guar) => { // E0071 might be caused by a spelling error, which will have // already caused an error message and probably a suggestion @@ -1394,7 +1400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path_span, E0071, "expected struct, variant or union type, found {}", - ty.normalized.sort_string(self.tcx) + normalized.sort_string(self.tcx) ) .span_label(path_span, "not a struct") .emit(), @@ -1802,23 +1808,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &QPath<'_>, path_span: Span, hir_id: hir::HirId, - ) -> (Res, RawTy<'tcx>) { + ) -> (Res, Ty<'tcx>) { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw); + let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); let ty = self.astconv().res_to_ty(self_ty, path, hir_id, true); - (path.res, self.handle_raw_ty(path_span, ty)) + (path.res, ty) } QPath::TypeRelative(ref qself, ref segment) => { let ty = self.to_ty(qself); let result = self .astconv() - .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true); + .associated_path_to_ty(hir_id, path_span, ty, qself, segment, true); let ty = result .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); - let ty = self.handle_raw_ty(path_span, ty); let result = result.map(|(_, kind, def_id)| (kind, def_id)); // Write back the new resolution. @@ -1827,8 +1832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) } QPath::LangItem(lang_item, span, id) => { - let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id, id); - (res, self.handle_raw_ty(path_span, ty)) + self.resolve_lang_item_path(lang_item, span, hir_id, id) } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 6a82b00211e45..0b391547e6d9f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -325,15 +325,3 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { Some(&self.infcx) } } - -/// Represents a user-provided type in the raw form (never normalized). -/// -/// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`, -/// and the API in this module, which expect `Ty` to be fully normalized. -#[derive(Clone, Copy, Debug)] -pub struct RawTy<'tcx> { - pub raw: Ty<'tcx>, - - /// The normalized form of `raw`, stored here for efficiency. - pub normalized: Ty<'tcx>, -} diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 4f45a24b216ea..d9b63fc6bb269 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -74,9 +74,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { let local_ty = match decl.ty { Some(ref ty) => { let o_ty = self.fcx.to_ty(&ty); - - let c_ty = - self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw)); + let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty)); debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty); self.fcx .typeck_results @@ -84,7 +82,9 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { .user_provided_types_mut() .insert(ty.hir_id, c_ty); - Some(o_ty.normalized) + // We only need to normalize here in the old solver + let normalized = self.fcx.normalize(ty.span, o_ty); + Some(normalized) } None => None, }; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c4d3cbc9faab0..ddb916864d752 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -51,7 +51,6 @@ use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; use crate::diverges::Diverges; use crate::expectation::Expectation; -use crate::fn_ctxt::RawTy; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 7c73f6a89cdb2..92ac36bb5aa77 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -395,7 +395,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.cfcx.fcx.astconv().ast_region_to_region(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.cfcx.to_ty(ty).raw.into() + self.cfcx.to_ty(ty).into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { self.cfcx.const_arg_to_const(&ct.value, param.def_id).into() diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index d8eb8c71b5ea3..df45d5146d9e4 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,4 +1,4 @@ -use crate::{errors, FnCtxt, RawTy}; +use crate::{errors, FnCtxt}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ @@ -874,7 +874,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &Pat<'tcx>, qpath: &hir::QPath<'_>, - path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), + path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), expected: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { diff --git a/tests/ui/traits/new-solver/normalize-path-for-method.rs b/tests/ui/traits/new-solver/normalize-path-for-method.rs new file mode 100644 index 0000000000000..b54e027d72a1c --- /dev/null +++ b/tests/ui/traits/new-solver/normalize-path-for-method.rs @@ -0,0 +1,18 @@ +// compile-flags: -Ztrait-solver=next +// run-pass + +trait Mirror { + type Assoc; +} +impl Mirror for T { + type Assoc = T; +} + +struct Foo; +impl Foo { + fn new() -> Self { Foo } +} + +fn main() { + ::Assoc::new(); +}