diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 5ee0cf9436019..6ac1df6a07928 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -2419,6 +2419,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Ok(None); } + // + // Select applicable inherent associated type candidates modulo regions. + // + // In contexts that have no inference context, just make a new one. // We do need a local variable to store it, though. let infcx_; @@ -2431,7 +2435,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; - let param_env = tcx.param_env(block.owner.to_def_id()); + // FIXME(inherent_associated_types): Acquiring the ParamEnv this early leads to cycle errors + // when inside of an ADT (#108491) or where clause. + let param_env = tcx.param_env(block.owner); let cause = ObligationCause::misc(span, block.owner.def_id); let mut fulfillment_errors = Vec::new(); @@ -2439,6 +2445,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let universe = infcx.create_next_universe(); // Regions are not considered during selection. + // FIXME(non_lifetime_binders): Here we are "truncating" or "flattening" the universes + // of type and const binders. Is that correct in the selection phase? See also #109505. let self_ty = tcx.replace_escaping_bound_vars_uncached( self_ty, FnMutDelegate { @@ -2454,41 +2462,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { candidates .iter() - .filter_map(|&(impl_, (assoc_item, def_scope))| { + .copied() + .filter(|&(impl_, _)| { infcx.probe(|_| { let ocx = ObligationCtxt::new_in_snapshot(&infcx); - let impl_ty = tcx.type_of(impl_); let impl_substs = infcx.fresh_item_substs(impl_); - let impl_ty = impl_ty.subst(tcx, impl_substs); + let impl_ty = tcx.type_of(impl_).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(inherent_associated_types): Should we use `eq` here? Method probing uses + // `sup` for this situtation, too. What for? To constrain inference variables? + if ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() + { + return false; + } // 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 = tcx.predicates_of(impl_).instantiate(tcx, impl_substs); let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds); - let impl_obligations = traits::predicates_for_generics( |_, _| cause.clone(), param_env, impl_bounds, ); - ocx.register_obligations(impl_obligations); let mut errors = ocx.select_where_possible(); if !errors.is_empty() { fulfillment_errors.append(&mut errors); - return None; + return false; } - // FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot. - Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs))) + true }) }) .collect() @@ -2497,24 +2504,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if applicable_candidates.len() > 1 { return Err(self.complain_about_ambiguous_inherent_assoc_type( name, - applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(), + applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(), span, )); } - if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() { + if let Some((impl_, (assoc_item, def_scope))) = 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 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); + // FIXME(fmease): Currently creating throwaway `parent_substs` to please + // `create_substs_for_associated_item`. Modify the latter instead (or sth. similar) to + // not require the parent substs logic. + let parent_substs = InternalSubsts::identity_for_item(tcx, impl_); + let substs = + self.create_substs_for_associated_item(span, assoc_item, segment, parent_substs); + let substs = tcx.mk_substs_from_iter( + std::iter::once(ty::GenericArg::from(self_ty)) + .chain(substs.into_iter().skip(parent_substs.len())), + ); - // 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); + let ty = tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(assoc_item, substs)); return Ok(Some((ty, assoc_item))); } diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 5ce8a83aad7f7..272177dfbd0f8 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -210,6 +210,19 @@ fn do_orphan_check_impl<'tcx>( NonlocalImpl::DisallowOther, ), + // ``` + // struct S(T); + // impl S { + // type This = T; + // } + // impl AutoTrait for S::This {} + // ``` + // FIXME(inherent_associated_types): The example code above currently leads to a cycle + ty::Alias(AliasKind::Inherent, _) => ( + LocalImpl::Disallow { problematic_kind: "associated type" }, + NonlocalImpl::DisallowOther, + ), + // type Opaque = impl Trait; // impl AutoTrait for Opaque {} ty::Alias(AliasKind::Opaque, _) => ( diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 821567c1d884e..5c7f7f10b17ec 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1948,7 +1948,7 @@ fn is_late_bound_map( ty::Param(param_ty) => { self.arg_is_constrained[param_ty.index as usize] = true; } - ty::Alias(ty::Projection, _) => return ControlFlow::Continue(()), + ty::Alias(ty::Projection | ty::Inherent, _) => return ControlFlow::Continue(()), _ => (), } t.super_visit_with(self) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c20fbfd1e4080..8df0166f76b4b 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -127,7 +127,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { // the def_id that this query was called with. We filter to only type and const args here // as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't // but it can't hurt to be safe ^^ - if let ty::Alias(ty::Projection, projection) = ty.kind() { + if let ty::Alias(ty::Projection | ty::Inherent, projection) = ty.kind() { let generics = tcx.generics_of(projection.def_id); let arg_index = segment diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index e18b0f082798b..9200c2aecf55c 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -59,7 +59,7 @@ struct ParameterCollector { impl<'tcx> TypeVisitor> for ParameterCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { match *t.kind() { - ty::Alias(ty::Projection, ..) if !self.include_nonconstraining => { + ty::Alias(ty::Projection | ty::Inherent, ..) if !self.include_nonconstraining => { // projections are not injective return ControlFlow::Continue(()); } diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index d53c429ca15ca..0cd2fc1aa299a 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -210,6 +210,9 @@ fn insert_required_predicates_to_be_wf<'tcx>( ); } + // FIXME(inherent_associated_types): Handle this case properly. + ty::Alias(ty::Inherent, _) => {} + _ => {} } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index 70ce45e21ea55..05e5d850bf958 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -843,7 +843,7 @@ fn find_param_in_ty<'tcx>( return true; } if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Projection, ..) = ty.kind() + && let ty::Alias(ty::Projection | ty::Inherent, ..) = ty.kind() { // This logic may seem a bit strange, but typically when // we have a projection type in a function signature, the diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 73a7bbebb6581..67f45f9aa3f0d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -300,7 +300,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { match ty.kind() { ty::Adt(adt_def, _) => Some(*adt_def), // FIXME(#104767): Should we handle bound regions here? - ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => { + ty::Alias(ty::Projection | ty::Inherent, _) if !ty.has_escaping_bound_vars() => { self.normalize(span, ty).ty_adt_def() } _ => None, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 30f0978d190c6..3741672e5683c 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2211,7 +2211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Adt(_, _) | ty::Str - | ty::Alias(ty::Projection, _) + | ty::Alias(ty::Projection | ty::Inherent, _) | ty::Param(_) => format!("{deref_ty}"), // we need to test something like <&[_]>::len or <(&[u32])>::len // and Vec::function(); diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 2a51439b0a973..08eec0707c0ad 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -127,7 +127,8 @@ impl<'tcx> InferCtxt<'tcx> { bug!() } - (_, ty::Alias(AliasKind::Projection, _)) | (ty::Alias(AliasKind::Projection, _), _) + (_, ty::Alias(AliasKind::Projection | AliasKind::Inherent, _)) + | (ty::Alias(AliasKind::Projection | AliasKind::Inherent, _), _) if self.tcx.trait_solver_next() => { relation.register_type_relate_obligation(a, b); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 98da5ba65b7b4..ce70f39cc4014 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2354,7 +2354,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), GenericKind::Alias(ref p) => match p.kind(self.tcx) { - ty::AliasKind::Projection => format!("the associated type `{}`", p), + ty::AliasKind::Projection | ty::AliasKind::Inherent => { + format!("the associated type `{}`", p) + } ty::AliasKind::Opaque => format!("the opaque type `{}`", p), }, }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index a31163519404c..064811bd29d77 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -71,9 +71,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { #traits-as-parameters", ); } - (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => { + (ty::Alias(ty::Projection | ty::Inherent, _), ty::Alias(ty::Projection | ty::Inherent, _)) => { diag.note("an associated type was expected, but a different one was found"); } + // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p)) if !tcx.is_impl_trait_in_trait(proj.def_id) => { @@ -222,7 +223,7 @@ impl Trait for X { diag.span_label(p_span, "this type parameter"); } } - (ty::Alias(ty::Projection, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => { + (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => { self.expected_projection( diag, proj_ty, @@ -231,7 +232,7 @@ impl Trait for X { cause.code(), ); } - (_, ty::Alias(ty::Projection, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => { + (_, ty::Alias(ty::Projection | ty::Inherent, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => { let msg = format!( "consider constraining the associated type `{}` to `{}`", values.found, values.expected, diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 334395945ea96..362b22b23a8da 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -549,6 +549,7 @@ impl<'tcx> InferCtxt<'tcx> { // We can't normalize associated types from `rustc_infer`, // but we can eagerly register inference variables for them. // FIXME(RPITIT): Don't replace RPITITs with inference vars. + // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. ty::Alias(ty::Projection, projection_ty) if !projection_ty.has_escaping_bound_vars() && !tcx.is_impl_trait_in_trait(projection_ty.def_id) => @@ -569,6 +570,7 @@ impl<'tcx> InferCtxt<'tcx> { hidden_ty } // FIXME(RPITIT): This can go away when we move to associated types + // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. ty::Alias( ty::Projection, ty::AliasTy { def_id: def_id2, substs: substs2, .. }, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 0b7a704eb57db..3025cce7ba7e5 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -62,6 +62,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::{LayoutError, LayoutOf}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef}; use rustc_session::config::ExpectedValues; use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason}; @@ -1442,6 +1443,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { // Bounds are respected for `type X = impl Trait` return; } + if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() { + // Bounds are respected for `type X = … Type::Inherent …` + return; + } // There must not be a where clause if type_alias_generics.predicates.is_empty() { return; @@ -1561,7 +1566,6 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::Clause; use rustc_middle::ty::PredicateKind::*; @@ -2898,6 +2902,7 @@ impl ClashingExternDeclarations { | (Generator(..), Generator(..)) | (GeneratorWitness(..), GeneratorWitness(..)) | (Alias(ty::Projection, ..), Alias(ty::Projection, ..)) + | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..)) | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false, // These definitely should have been caught above. diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index a6ba742201a3b..125b4dc550310 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1119,14 +1119,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, // so they are currently ignored for the purposes of this lint. - ty::Param(..) | ty::Alias(ty::Projection, ..) + ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..) if matches!(self.mode, CItemKind::Definition) => { FfiSafe } ty::Param(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Infer(..) | ty::Bound(..) | ty::Error(_) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b425c7600ac68..d5b185e45d6b4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1821,6 +1821,16 @@ rustc_queries! { desc { "normalizing `{}`", goal.value.value } } + /// Do not call this query directly: invoke `normalize` instead. + query normalize_inherent_projection_ty( + goal: CanonicalProjectionGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{}`", goal.value.value } + } + /// Do not call this query directly: invoke `try_normalize_erasing_regions` instead. query try_normalize_generic_arg_after_erasing_regions( goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c255057287953..8aea2d8aedf98 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1848,7 +1848,17 @@ impl<'tcx> TyCtxt<'tcx> { let substs = substs.into_iter().map(Into::into); #[cfg(debug_assertions)] { - let n = self.generics_of(_def_id).count(); + let generics = self.generics_of(_def_id); + + let n = if let DefKind::AssocTy = self.def_kind(_def_id) + && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id)) + { + // If this is an inherent projection. + + generics.params.len() + 1 + } else { + generics.count() + }; assert_eq!( (n, Some(n)), substs.size_hint(), @@ -2009,7 +2019,7 @@ impl<'tcx> TyCtxt<'tcx> { debug_assert_matches!( (kind, self.def_kind(alias_ty.def_id)), (ty::Opaque, DefKind::OpaqueTy) - | (ty::Projection, DefKind::AssocTy) + | (ty::Projection | ty::Inherent, DefKind::AssocTy) | (ty::Opaque | ty::Projection, DefKind::ImplTraitPlaceholder) ); self.mk_ty_from_kind(Alias(kind, alias_ty)) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 1be61e16dbed8..49ab9b79e96f3 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -265,7 +265,7 @@ impl<'tcx> Ty<'tcx> { ty::Infer(ty::FreshTy(_)) => "fresh type".into(), ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), - ty::Alias(ty::Projection, _) => "associated type".into(), + ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(), ty::Param(p) => format!("type parameter `{p}`").into(), ty::Alias(ty::Opaque, ..) => if tcx.ty_is_opaque_future(self) { "future".into() } else { "opaque type".into() }, ty::Error(_) => "type error".into(), @@ -312,7 +312,7 @@ impl<'tcx> Ty<'tcx> { ty::Tuple(..) => "tuple".into(), ty::Placeholder(..) => "higher-ranked type".into(), ty::Bound(..) => "bound type variable".into(), - ty::Alias(ty::Projection, _) => "associated type".into(), + ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(), ty::Param(_) => "type parameter".into(), ty::Alias(ty::Opaque, ..) => "opaque type".into(), } diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 68002bfcfbd13..d64875a9f00e8 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -176,14 +176,14 @@ impl FlagComputation { self.add_substs(substs); } - &ty::Alias(ty::Projection, data) => { - self.add_flags(TypeFlags::HAS_TY_PROJECTION); - self.add_alias_ty(data); - } + &ty::Alias(kind, data) => { + self.add_flags(match kind { + ty::Projection => TypeFlags::HAS_TY_PROJECTION, + ty::Inherent => TypeFlags::HAS_TY_INHERENT, + ty::Opaque => TypeFlags::HAS_TY_OPAQUE, + }); - &ty::Alias(ty::Opaque, ty::AliasTy { substs, .. }) => { - self.add_flags(TypeFlags::HAS_TY_OPAQUE); - self.add_substs(substs); + self.add_alias_ty(data); } &ty::Dynamic(obj, r, _) => { diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 92a040068dd75..9e672004cf9c0 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -113,6 +113,12 @@ impl<'tcx> Ty<'tcx> { } Never => InhabitedPredicate::False, Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self), + // FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above. + // However it's unclear if the substs passed to `InhabitedPredicate::subst` are of the correct + // format, i.e. don't contain parent substs. If you hit this case, please verify this beforehand. + Alias(ty::Inherent, _) => { + bug!("unimplemented: inhabitedness checking for inherent projections") + } Tuple(tys) if tys.is_empty() => InhabitedPredicate::True, // use a query for more complex cases Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self), diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index f2a2e67cf82dc..47cf48f46cf89 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -324,7 +324,7 @@ impl<'tcx> SizeSkeleton<'tcx> { let non_zero = !ty.is_unsafe_ptr(); let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); match tail.kind() { - ty::Param(_) | ty::Alias(ty::Projection, _) => { + ty::Param(_) | ty::Alias(ty::Projection | ty::Inherent, _) => { debug_assert!(tail.has_non_region_param()); Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) }) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 88b084bbccbdc..f882f54d62811 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1004,7 +1004,7 @@ impl<'tcx> Term<'tcx> { match self.unpack() { TermKind::Ty(ty) => match ty.kind() { ty::Alias(kind, alias_ty) => match kind { - AliasKind::Projection => Some(*alias_ty), + AliasKind::Projection | AliasKind::Inherent => Some(*alias_ty), AliasKind::Opaque => None, }, _ => None, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index b7e780b94efee..926172ff828c7 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -729,7 +729,7 @@ pub trait PrettyPrinter<'tcx>: ty::Foreign(def_id) => { p!(print_def_path(def_id, &[])); } - ty::Alias(ty::Projection, ref data) => { + ty::Alias(ty::Projection | ty::Inherent, ref data) => { if !(self.should_print_verbose() || NO_QUERIES.with(|q| q.get())) && self.tcx().is_impl_trait_in_trait(data.def_id) { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 7f28ed6c26337..da43475941ee5 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -550,6 +550,11 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>( Ok(tcx.mk_projection(projection_ty.def_id, projection_ty.substs)) } + (&ty::Alias(ty::Inherent, a_data), &ty::Alias(ty::Inherent, b_data)) => { + let alias_ty = relation.relate(a_data, b_data)?; + Ok(tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(alias_ty.def_id, alias_ty.substs))) + } + ( &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, substs: a_substs, .. }), &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, substs: b_substs, .. }), diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 646384eebc888..8d0737e1eeeeb 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1190,9 +1190,9 @@ where /// Represents the projection of an associated type. /// -/// For a projection, this would be `>::N`. -/// -/// For an opaque type, there is no explicit syntax. +/// * For a projection, this would be `>::N<...>`. +/// * For an inherent projection, this would be `Ty::N<...>`. +/// * For an opaque type, there is no explicit syntax. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct AliasTy<'tcx> { @@ -1201,12 +1201,16 @@ pub struct AliasTy<'tcx> { /// For a projection, these are the substitutions for the trait and the /// GAT substitutions, if there are any. /// + /// For an inherent projection, they consist of the self type and the GAT substitutions, + /// if there are any. + /// /// For RPIT the substitutions are for the generics of the function, /// while for TAIT it is used for the generic parameters of the alias. pub substs: SubstsRef<'tcx>, - /// The `DefId` of the `TraitItem` for the associated type `N` if this is a projection, - /// or the `OpaqueType` item if this is an opaque. + /// The `DefId` of the `TraitItem` or `ImplItem` for the associated type `N` depending on whether + /// this is a projection or an inherent projection or the `DefId` of the `OpaqueType` item if + /// this is an opaque. /// /// During codegen, `tcx.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. @@ -1224,6 +1228,7 @@ pub struct AliasTy<'tcx> { impl<'tcx> AliasTy<'tcx> { pub fn kind(self, tcx: TyCtxt<'tcx>) -> ty::AliasKind { match tcx.def_kind(self.def_id) { + DefKind::AssocTy if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(self.def_id)) => ty::Inherent, DefKind::AssocTy | DefKind::ImplTraitPlaceholder => ty::Projection, DefKind::OpaqueTy => ty::Opaque, kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), @@ -1236,6 +1241,17 @@ impl<'tcx> AliasTy<'tcx> { } /// The following methods work only with associated type projections. +impl<'tcx> AliasTy<'tcx> { + pub fn self_ty(self) -> Ty<'tcx> { + self.substs.type_at(0) + } + + pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1))) + } +} + +/// The following methods work only with trait associated type projections. impl<'tcx> AliasTy<'tcx> { pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { match tcx.def_kind(self.def_id) { @@ -1274,13 +1290,28 @@ impl<'tcx> AliasTy<'tcx> { let def_id = self.trait_def_id(tcx); ty::TraitRef::new(tcx, def_id, self.substs.truncate_to(tcx, tcx.generics_of(def_id))) } +} - pub fn self_ty(self) -> Ty<'tcx> { - self.substs.type_at(0) - } +/// The following methods work only with inherent associated type projections. +impl<'tcx> AliasTy<'tcx> { + /// Transform the substitutions to have the given `impl` substs as the base and the GAT substs on top of that. + /// + /// Does the following transformation: + /// + /// ```text + /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m] + /// + /// I_i impl subst + /// P_j GAT subst + /// ``` + pub fn rebase_substs_onto_impl( + self, + impl_substs: ty::SubstsRef<'tcx>, + tcx: TyCtxt<'tcx>, + ) -> ty::SubstsRef<'tcx> { + debug_assert_eq!(self.kind(tcx), ty::Inherent); - pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { - tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1))) + tcx.mk_substs_from_iter(impl_substs.into_iter().chain(self.substs.into_iter().skip(1))) } } diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 5eaa58d69ed66..520bb55e031c7 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -49,6 +49,9 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable> { fn has_projections(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PROJECTION) } + fn has_inherent_projections(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_TY_INHERENT) + } fn has_opaque_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index b738ce35ada80..f27b8d9df1aa2 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -243,6 +243,39 @@ where // This will also visit substs if necessary, so we don't need to recurse. return self.visit_projection_ty(proj); } + ty::Alias(ty::Inherent, data) => { + if self.def_id_visitor.skip_assoc_tys() { + // Visitors searching for minimal visibility/reachability want to + // conservatively approximate associated types like `Type::Alias` + // as visible/reachable even if `Type` is private. + // Ideally, associated types should be substituted in the same way as + // free type aliases, but this isn't done yet. + return ControlFlow::Continue(()); + } + + self.def_id_visitor.visit_def_id( + data.def_id, + "associated type", + &LazyDefPathStr { def_id: data.def_id, tcx }, + )?; + + struct LazyDefPathStr<'tcx> { + def_id: DefId, + tcx: TyCtxt<'tcx>, + } + impl<'tcx> fmt::Display for LazyDefPathStr<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.tcx.def_path_str(self.def_id)) + } + } + + // This will also visit substs if necessary, so we don't need to recurse. + return if self.def_id_visitor.shallow() { + ControlFlow::Continue(()) + } else { + data.substs.iter().try_for_each(|subst| subst.visit_with(self)) + }; + } ty::Dynamic(predicates, ..) => { // All traits in the list are considered the "primary" part of the type // and are visited by shallow visitors. diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 14eb4a5502d5c..8fea3fc140d72 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -20,3 +20,5 @@ trait_selection_negative_positive_conflict = found both positive and negative im .negative_implementation_in_crate = negative implementation in crate `{$negative_impl_cname}` .positive_implementation_here = positive implementation here .positive_implementation_in_crate = positive implementation in crate `{$positive_impl_cname}` + +trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}` diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index df7c4df1868c7..54e22cc3d7fe2 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -89,3 +89,11 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> { diag } } + +#[derive(Diagnostic)] +#[diag(trait_selection_inherent_projection_normalization_overflow)] +pub struct InherentProjectionNormalizationOverflow { + #[primary_span] + pub span: Span, + pub ty: String, +} diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 996dc329dcb9b..0ede32c753c69 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -33,7 +33,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 6c98fadd1482a..04b38edc1265f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -655,7 +655,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) => Some(Err(NoSolution)), ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"), diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index b7690f79933d9..402b09419c877 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -673,7 +673,7 @@ impl<'tcx> TypeVisitor> for OrphanChecker<'tcx> { | ty::RawPtr(..) | ty::Never | ty::Tuple(..) - | ty::Alias(ty::Projection, ..) => self.found_non_local_ty(ty), + | ty::Alias(ty::Projection | ty::Inherent, ..) => self.found_non_local_ty(ty), ty::Param(..) => self.found_param_ty(ty), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index afb64da8b6173..c9e2ed092d160 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1687,13 +1687,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::Tuple(..) => Some(10), ty::Param(..) => Some(11), ty::Alias(ty::Projection, ..) => Some(12), - ty::Alias(ty::Opaque, ..) => Some(13), - ty::Never => Some(14), - ty::Adt(..) => Some(15), - ty::Generator(..) => Some(16), - ty::Foreign(..) => Some(17), - ty::GeneratorWitness(..) => Some(18), - ty::GeneratorWitnessMIR(..) => Some(19), + ty::Alias(ty::Inherent, ..) => Some(13), + ty::Alias(ty::Opaque, ..) => Some(14), + ty::Never => Some(15), + ty::Adt(..) => Some(16), + ty::Generator(..) => Some(17), + ty::Foreign(..) => Some(18), + ty::GeneratorWitness(..) => Some(19), + ty::GeneratorWitnessMIR(..) => Some(20), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index d8e5725d3cafa..b162e5b8995db 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -49,7 +49,8 @@ pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::ObjectSafetyViolation; -pub use self::project::{normalize_projection_type, NormalizeExt}; +pub use self::project::NormalizeExt; +pub use self::project::{normalize_inherent_projection, normalize_projection_type}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; pub use self::specialize::specialization_graph::FutureCompatOverlapError; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 8c74860cdf341..8e684b7ac239b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -16,6 +16,7 @@ use super::{ }; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; +use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::traits::error_reporting::TypeErrCtxtExt as _; @@ -370,10 +371,14 @@ pub(crate) fn needs_normalization<'tcx, T: TypeVisitable>>( reveal: Reveal, ) -> bool { match reveal { - Reveal::UserFacing => value - .has_type_flags(ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_CT_PROJECTION), + Reveal::UserFacing => value.has_type_flags( + ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_CT_PROJECTION, + ), Reveal::All => value.has_type_flags( ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT | ty::TypeFlags::HAS_TY_OPAQUE | ty::TypeFlags::HAS_CT_PROJECTION, ), @@ -616,6 +621,51 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx ); normalized_ty } + + ty::Inherent if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + + let data = data.fold_with(self); + + // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement` + // here like `ty::Projection`? + normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + &mut self.obligations, + ) + } + + ty::Inherent => { + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let ty = normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + &mut self.obligations, + ); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + ty, + ) + } } } @@ -1204,6 +1254,115 @@ fn normalize_to_error<'a, 'tcx>( Normalized { value: new_value, obligations: vec![trait_obligation] } } +/// Confirm and normalize the given inherent projection. +#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] +pub fn normalize_inherent_projection<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec>, +) -> Ty<'tcx> { + let tcx = selcx.tcx(); + + if !tcx.recursion_limit().value_within_limit(depth) { + // Halt compilation because it is important that overflows never be masked. + tcx.sess.emit_fatal(InherentProjectionNormalizationOverflow { + span: cause.span, + ty: alias_ty.to_string(), + }); + } + + let substs = compute_inherent_assoc_ty_substs( + selcx, + param_env, + alias_ty, + cause.clone(), + depth, + obligations, + ); + + // Register the obligations arising from the impl and from the associated type itself. + let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, substs); + for (predicate, span) in predicates { + let predicate = normalize_with_depth_to( + selcx, + param_env, + cause.clone(), + depth + 1, + predicate, + obligations, + ); + + let nested_cause = ObligationCause::new( + cause.span, + cause.body_id, + // FIXME(inherent_associated_types): Since we can't pass along the self type to the + // cause code, inherent projections will be printed with identity substitutions in + // diagnostics which is not ideal. + // Consider creating separate cause codes for this specific situation. + if span.is_dummy() { + super::ItemObligation(alias_ty.def_id) + } else { + super::BindingObligation(alias_ty.def_id, span) + }, + ); + + obligations.push(Obligation::with_depth( + tcx, + nested_cause, + depth + 1, + param_env, + predicate, + )); + } + + let ty = tcx.type_of(alias_ty.def_id).subst(tcx, substs); + + let mut ty = selcx.infcx.resolve_vars_if_possible(ty); + if ty.has_projections() { + ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations); + } + + ty +} + +pub fn compute_inherent_assoc_ty_substs<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec>, +) -> ty::SubstsRef<'tcx> { + let tcx = selcx.tcx(); + + let impl_def_id = tcx.parent(alias_ty.def_id); + let impl_substs = selcx.infcx.fresh_substs_for_item(cause.span, impl_def_id); + + let impl_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs); + let impl_ty = + normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations); + + // Infer the generic parameters of the impl by unifying the + // impl type with the self type of the projection. + let self_ty = alias_ty.self_ty(); + match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { + Ok(mut ok) => obligations.append(&mut ok.obligations), + Err(_) => { + tcx.sess.delay_span_bug( + cause.span, + format!( + "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" + ), + ); + } + } + + alias_ty.rebase_substs_onto_impl(impl_substs, tcx) +} + enum Projected<'tcx> { Progress(Progress<'tcx>), NoProgress(ty::Term<'tcx>), diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index a986a9b6a71b1..8bf934cb78ae1 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -257,11 +257,11 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> ty::Opaque => ty.try_super_fold_with(self)?, - ty::Projection => { + ty::Projection | ty::Inherent => { // See note in `rustc_trait_selection::traits::project` - let tcx = self.infcx.tcx; let infcx = self.infcx; + let tcx = infcx.tcx; // Just an optimization: When we don't have escaping bound vars, // we don't need to replace them with placeholders. let (data, maps) = if data.has_escaping_bound_vars() { @@ -276,12 +276,15 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> let mut orig_values = OriginalQueryValues::default(); // HACK(matthewjasper) `'static` is special-cased in selection, // so we cannot canonicalize it. - let c_data = self - .infcx + let c_data = infcx .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values); debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: orig_values = {:#?}", orig_values); - let result = tcx.normalize_projection_ty(c_data)?; + let result = match kind { + ty::Projection => tcx.normalize_projection_ty(c_data), + ty::Inherent => tcx.normalize_inherent_projection_ty(c_data), + _ => unreachable!(), + }?; // We don't expect ambiguity. if result.is_ambiguous() { // Rustdoc normalizes possibly not well-formed types, so only @@ -294,8 +297,8 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> } return Err(NoSolution); } - let InferOk { value: result, obligations } = - self.infcx.instantiate_query_response_and_region_obligations( + let InferOk { value: result, obligations } = infcx + .instantiate_query_response_and_region_obligations( self.cause, self.param_env, &orig_values, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 33f502f818285..a8fb55df2d302 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -498,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // this trait and type. } ty::Param(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Placeholder(..) | ty::Bound(..) => { // In these cases, we don't know what the actual diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 422285d947400..616187b69dde4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1268,7 +1268,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If we have a projection type, make sure to normalize it so we replace it // with a fresh infer variable - ty::Alias(ty::Projection, ..) => { + ty::Alias(ty::Projection | ty::Inherent, ..) => { let predicate = normalize_with_depth_to( self, obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 246d3ea2ef298..e4f5a84f4244e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2315,7 +2315,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble constituent types of unexpected type: {:?}", t); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 22710c7c059a9..086ab32b52007 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -58,15 +58,8 @@ pub fn obligations<'tcx>( GenericArgKind::Lifetime(..) => return Some(Vec::new()), }; - let mut wf = WfPredicates { - tcx: infcx.tcx, - param_env, - body_id, - span, - out: vec![], - recursion_depth, - item: None, - }; + let mut wf = + WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None }; wf.compute(arg); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); @@ -91,7 +84,7 @@ pub fn unnormalized_obligations<'tcx>( debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id: CRATE_DEF_ID, span: DUMMY_SP, @@ -116,7 +109,7 @@ pub fn trait_obligations<'tcx>( item: &'tcx hir::Item<'tcx>, ) -> Vec> { let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id, span, @@ -138,7 +131,7 @@ pub fn predicate_obligations<'tcx>( span: Span, ) -> Vec> { let mut wf = WfPredicates { - tcx: infcx.tcx, + infcx, param_env, body_id, span, @@ -190,8 +183,8 @@ pub fn predicate_obligations<'tcx>( wf.normalize(infcx) } -struct WfPredicates<'tcx> { - tcx: TyCtxt<'tcx>, +struct WfPredicates<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, span: Span, @@ -290,9 +283,9 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( } } -impl<'tcx> WfPredicates<'tcx> { +impl<'a, 'tcx> WfPredicates<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx + self.infcx.tcx } fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { @@ -325,7 +318,7 @@ impl<'tcx> WfPredicates<'tcx> { /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. fn compute_trait_pred(&mut self, trait_pred: &ty::TraitPredicate<'tcx>, elaborate: Elaborate) { - let tcx = self.tcx; + let tcx = self.tcx(); let trait_ref = &trait_pred.trait_ref; // Negative trait predicates don't require supertraits to hold, just @@ -369,7 +362,6 @@ impl<'tcx> WfPredicates<'tcx> { self.out.extend(obligations); } - let tcx = self.tcx(); self.out.extend( trait_ref .substs @@ -436,13 +428,45 @@ impl<'tcx> WfPredicates<'tcx> { let obligations = self.nominal_obligations_without_const(data.def_id, data.substs); self.out.extend(obligations); + self.compute_projection_substs(data.substs); + } + + fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { + // An inherent projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its substs are wf + // + // (*) The predicates of an inherent associated type include the + // predicates of the impl that it's contained in. + + if !data.self_ty().has_escaping_bound_vars() { + // FIXME(inherent_associated_types): Should this happen inside of a snapshot? + // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm! + let substs = traits::project::compute_inherent_assoc_ty_substs( + &mut traits::SelectionContext::new(self.infcx), + self.param_env, + data, + self.cause(traits::WellFormed(None)), + self.recursion_depth, + &mut self.out, + ); + // Inherent projection types do not require const predicates. + let obligations = self.nominal_obligations_without_const(data.def_id, substs); + self.out.extend(obligations); + } + + self.compute_projection_substs(data.substs); + } + + fn compute_projection_substs(&mut self, substs: SubstsRef<'tcx>) { let tcx = self.tcx(); let cause = self.cause(traits::WellFormed(None)); let param_env = self.param_env; let depth = self.recursion_depth; self.out.extend( - data.substs + substs .iter() .filter(|arg| { matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) @@ -464,9 +488,9 @@ impl<'tcx> WfPredicates<'tcx> { if !subty.has_escaping_bound_vars() { let cause = self.cause(cause); let trait_ref = - ty::TraitRef::from_lang_item(self.tcx, LangItem::Sized, cause.span, [subty]); + ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [subty]); self.out.push(traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, @@ -605,6 +629,10 @@ impl<'tcx> WfPredicates<'tcx> { walker.skip_current_subtree(); // Subtree handled by compute_projection. self.compute_projection(data); } + ty::Alias(ty::Inherent, data) => { + walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection. + self.compute_inherent_projection(data); + } ty::Adt(def, substs) => { // WfNominalType @@ -697,7 +725,7 @@ impl<'tcx> WfPredicates<'tcx> { // All of the requirements on type parameters // have already been checked for `impl Trait` in // return position. We do need to check type-alias-impl-trait though. - if self.tcx.is_type_alias_impl_trait(def_id) { + if self.tcx().is_type_alias_impl_trait(def_id) { let obligations = self.nominal_obligations(def_id, substs); self.out.extend(obligations); } @@ -767,15 +795,15 @@ impl<'tcx> WfPredicates<'tcx> { substs: SubstsRef<'tcx>, remap_constness: bool, ) -> Vec> { - let predicates = self.tcx.predicates_of(def_id); + let predicates = self.tcx().predicates_of(def_id); let mut origins = vec![def_id; predicates.predicates.len()]; let mut head = predicates; while let Some(parent) = head.parent { - head = self.tcx.predicates_of(parent); + head = self.tcx().predicates_of(parent); origins.extend(iter::repeat(parent).take(head.predicates.len())); } - let predicates = predicates.instantiate(self.tcx, substs); + let predicates = predicates.instantiate(self.tcx(), substs); trace!("{:#?}", predicates); debug_assert_eq!(predicates.predicates.len(), origins.len()); @@ -788,10 +816,10 @@ impl<'tcx> WfPredicates<'tcx> { }; let cause = self.cause(code); if remap_constness { - pred = pred.without_const(self.tcx); + pred = pred.without_const(self.tcx()); } traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, @@ -856,7 +884,7 @@ impl<'tcx> WfPredicates<'tcx> { // Note: in fact we only permit builtin traits, not `Bar<'d>`, I // am looking forward to the future here. if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { - let implicit_bounds = object_region_bounds(self.tcx, data); + let implicit_bounds = object_region_bounds(self.tcx(), data); let explicit_bound = region; @@ -866,7 +894,7 @@ impl<'tcx> WfPredicates<'tcx> { let outlives = ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); self.out.push(traits::Obligation::with_depth( - self.tcx, + self.tcx(), cause, self.recursion_depth, self.param_env, diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 4d225e36b22cd..2f9e480d8bd52 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -372,6 +372,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { substitution: substs.lower_into(interner), })) } + ty::Alias(ty::Inherent, _) => unimplemented!(), ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id: chalk_ir::OpaqueTyId(def_id), diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index e805eb4282119..36d80a06ee7e9 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -10,7 +10,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext}; use std::sync::atomic::Ordering; pub(crate) fn provide(p: &mut Providers) { - *p = Providers { normalize_projection_ty, ..*p }; + *p = Providers { normalize_projection_ty, normalize_inherent_projection_ty, ..*p }; } fn normalize_projection_ty<'tcx>( @@ -42,3 +42,30 @@ fn normalize_projection_ty<'tcx>( }, ) } + +fn normalize_inherent_projection_ty<'tcx>( + tcx: TyCtxt<'tcx>, + goal: CanonicalProjectionGoal<'tcx>, +) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { + debug!("normalize_provider(goal={:#?})", goal); + + tcx.infer_ctxt().enter_canonical_trait_query( + &goal, + |ocx, ParamEnvAnd { param_env, value: goal }| { + let selcx = &mut SelectionContext::new(ocx.infcx); + let cause = ObligationCause::dummy(); + let mut obligations = vec![]; + let answer = traits::normalize_inherent_projection( + selcx, + param_env, + goal, + cause, + 0, + &mut obligations, + ); + ocx.register_obligations(obligations); + + Ok(NormalizationResult { normalized_ty: answer }) + }, + ) +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 1e91e26e2afe9..7e5a4d1c73532 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -229,29 +229,32 @@ bitflags! { /// Does this have `Projection`? const HAS_TY_PROJECTION = 1 << 10; + /// Does this have `Inherent`? + const HAS_TY_INHERENT = 1 << 11; /// Does this have `Opaque`? - const HAS_TY_OPAQUE = 1 << 11; + const HAS_TY_OPAQUE = 1 << 12; /// Does this have `ConstKind::Unevaluated`? - const HAS_CT_PROJECTION = 1 << 12; + const HAS_CT_PROJECTION = 1 << 13; /// Could this type be normalized further? const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits | TypeFlags::HAS_TY_OPAQUE.bits + | TypeFlags::HAS_TY_INHERENT.bits | TypeFlags::HAS_CT_PROJECTION.bits; /// Is an error type/const reachable? - const HAS_ERROR = 1 << 13; + const HAS_ERROR = 1 << 14; /// Does this have any region that "appears free" in the type? /// Basically anything but `ReLateBound` and `ReErased`. - const HAS_FREE_REGIONS = 1 << 14; + const HAS_FREE_REGIONS = 1 << 15; /// Does this have any `ReLateBound` regions? - const HAS_RE_LATE_BOUND = 1 << 15; + const HAS_RE_LATE_BOUND = 1 << 16; /// Does this have any `Bound` types? - const HAS_TY_LATE_BOUND = 1 << 16; + const HAS_TY_LATE_BOUND = 1 << 17; /// Does this have any `ConstKind::Bound` consts? - const HAS_CT_LATE_BOUND = 1 << 17; + const HAS_CT_LATE_BOUND = 1 << 18; /// Does this have any bound variables? /// Used to check if a global bound is safe to evaluate. const HAS_LATE_BOUND = TypeFlags::HAS_RE_LATE_BOUND.bits @@ -259,20 +262,20 @@ bitflags! { | TypeFlags::HAS_CT_LATE_BOUND.bits; /// Does this have any `ReErased` regions? - const HAS_RE_ERASED = 1 << 18; + const HAS_RE_ERASED = 1 << 19; /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 19; + const STILL_FURTHER_SPECIALIZABLE = 1 << 20; /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 20; + const HAS_TY_FRESH = 1 << 21; /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 21; + const HAS_CT_FRESH = 1 << 22; /// Does this have `Generator` or `GeneratorWitness`? - const HAS_TY_GENERATOR = 1 << 22; + const HAS_TY_GENERATOR = 1 << 23; } } diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index 4c1f2dd0e5334..f7344bacc028c 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -37,6 +37,7 @@ pub enum DynKind { #[derive(Encodable, Decodable, HashStable_Generic)] pub enum AliasKind { Projection, + Inherent, Opaque, } diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index fb32b6ef1d3f5..baf2b0a858529 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -556,7 +556,10 @@ where WherePredicate::EqPredicate { lhs, rhs, bound_params } => { match *lhs { Type::QPath(box QPathData { - ref assoc, ref self_type, ref trait_, .. + ref assoc, + ref self_type, + trait_: Some(ref trait_), + .. }) => { let ty = &*self_type; let mut new_trait = trait_.clone(); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 951f54e93663c..c852f9cca2bfd 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -706,7 +706,12 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: g.where_predicates.retain(|pred| match pred { clean::WherePredicate::BoundPredicate { - ty: clean::QPath(box clean::QPathData { self_type: clean::Generic(ref s), trait_, .. }), + ty: + clean::QPath(box clean::QPathData { + self_type: clean::Generic(ref s), + trait_: Some(trait_), + .. + }), bounds, .. } => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 23449a25c3a5e..657f3c9ec4577 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -441,7 +441,7 @@ fn clean_projection<'tcx>( assoc: projection_to_path_segment(ty, cx), should_show_cast, self_type, - trait_, + trait_: Some(trait_), })) } @@ -1330,7 +1330,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let mut bounds: Vec = Vec::new(); generics.where_predicates.retain_mut(|pred| match *pred { WherePredicate::BoundPredicate { - ty: QPath(box QPathData { ref assoc, ref self_type, ref trait_, .. }), + ty: + QPath(box QPathData { + ref assoc, + ref self_type, + trait_: Some(ref trait_), + .. + }), bounds: ref mut pred_bounds, .. } => { @@ -1492,25 +1498,30 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx), should_show_cast, self_type, - trait_, + trait_: Some(trait_), })) } hir::QPath::TypeRelative(qself, segment) => { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - let res = match ty.kind() { + let self_type = clean_ty(qself, cx); + + let (trait_, should_show_cast) = match ty.kind() { ty::Alias(ty::Projection, proj) => { - Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id) + let res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); + let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); + register_res(cx, trait_.res); + let self_def_id = res.opt_def_id(); + let should_show_cast = + compute_should_show_cast(self_def_id, &trait_, &self_type); + + (Some(trait_), should_show_cast) } + ty::Alias(ty::Inherent, _) => (None, false), // Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s. ty::Error(_) => return Type::Infer, - // Otherwise, this is an inherent associated type. - _ => return clean_middle_ty(ty::Binder::dummy(ty), cx, None), + _ => bug!("clean: expected associated type, found `{ty:?}`"), }; - let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); - register_res(cx, trait_.res); - let self_def_id = res.opt_def_id(); - let self_type = clean_ty(qself, cx); - let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type); + Type::QPath(Box::new(QPathData { assoc: clean_path_segment(segment, cx), should_show_cast, @@ -1836,6 +1847,29 @@ pub(crate) fn clean_middle_ty<'tcx>( clean_projection(bound_ty.rebind(*data), cx, parent_def_id) } + ty::Alias(ty::Inherent, alias_ty) => { + let alias_ty = bound_ty.rebind(alias_ty); + let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None); + + Type::QPath(Box::new(QPathData { + assoc: PathSegment { + name: cx.tcx.associated_item(alias_ty.skip_binder().def_id).name, + args: GenericArgs::AngleBracketed { + args: substs_to_args( + cx, + alias_ty.map_bound(|ty| ty.substs.as_slice()), + true, + ) + .into(), + bindings: Default::default(), + }, + }, + should_show_cast: false, + self_type, + trait_: None, + })) + } + ty::Param(ref p) => { if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { ImplTrait(bounds) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 7371b44465bab..38664c3e359a6 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1660,7 +1660,7 @@ impl Type { pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> { if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self { - Some((self_type, trait_.def_id(), assoc.clone())) + Some((self_type, trait_.as_ref()?.def_id(), assoc.clone())) } else { None } @@ -1704,7 +1704,7 @@ pub(crate) struct QPathData { pub self_type: Type, /// FIXME: compute this field on demand. pub should_show_cast: bool, - pub trait_: Path, + pub trait_: Option, } /// A primitive (aka, builtin) type. diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 1c6810bdaf9be..d963d6092c48f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1116,14 +1116,17 @@ fn fmt_type<'cx>( ref trait_, should_show_cast, }) => { + // FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719), + // we need to surround them with angle brackets in some cases (e.g. `::P`). + if f.alternate() { - if should_show_cast { + if let Some(trait_) = trait_ && should_show_cast { write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? } else { write!(f, "{:#}::", self_type.print(cx))? } } else { - if should_show_cast { + if let Some(trait_) = trait_ && should_show_cast { write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? } else { write!(f, "{}::", self_type.print(cx))? @@ -1139,15 +1142,36 @@ fn fmt_type<'cx>( // the ugliness comes from inlining across crates where // everything comes in as a fully resolved QPath (hard to // look at). - if !f.alternate() && let Ok((url, _, path)) = href(trait_.def_id(), cx) { - write!( - f, - "{name}", - shortty = ItemType::AssocType, - name = assoc.name, - path = join_with_double_colon(&path), - ) + if !f.alternate() { + // FIXME(inherent_associated_types): We always link to the very first associated + // type (in respect to source order) that bears the given name (`assoc.name`) and that is + // affiliated with the computed `DefId`. This is obviously incorrect when we have + // multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself + // through here and map it to the corresponding HTML ID that was generated by + // `render::Context::derive_id` when the impl blocks were rendered. + // There is no such mapping unfortunately. + // As a hack, we could badly imitate `derive_id` here by keeping *count* when looking + // for the assoc ty `DefId` in `tcx.associated_items(self_ty_did).in_definition_order()` + // considering privacy, `doc(hidden)`, etc. + // I don't feel like that right now :cold_sweat:. + + let parent_href = match trait_ { + Some(trait_) => href(trait_.def_id(), cx).ok(), + None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()), + }; + + if let Some((url, _, path)) = parent_href { + write!( + f, + "{name}", + shortty = ItemType::AssocType, + name = assoc.name, + path = join_with_double_colon(&path), + ) + } else { + write!(f, "{}", assoc.name) + } } else { write!(f, "{}", assoc.name) }?; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index e09c6480060d0..d6773169639c3 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2202,7 +2202,9 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec { } clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => { work.push_back(self_type); - process_path(trait_.def_id()); + if let Some(trait_) = trait_ { + process_path(trait_.def_id()); + } } _ => {} } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index b5bebb7059380..b1cef20b434a6 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -574,7 +574,7 @@ impl FromWithTcx for Type { name: assoc.name.to_string(), args: Box::new(assoc.args.into_tcx(tcx)), self_type: Box::new(self_type.into_tcx(tcx)), - trait_: trait_.into_tcx(tcx), + trait_: trait_.map(|trait_| trait_.into_tcx(tcx)), }, } } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 3cf8ceed62036..3556834071fc9 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 24; +pub const FORMAT_VERSION: u32 = 25; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -581,13 +581,15 @@ pub enum Type { #[serde(rename = "type")] type_: Box, }, - /// `::Name` or associated types like `T::Item` where `T: Iterator` + /// Associated types like `::Name` and `T::Item` where + /// `T: Iterator` or inherent associated types like `Struct::Name`. QualifiedPath { name: String, args: Box, self_type: Box, + /// `None` iff this is an *inherent* associated type. #[serde(rename = "trait")] - trait_: Path, + trait_: Option, }, } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 7f3f26bed7c7d..b27ffe73ffda4 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1424,6 +1424,7 @@ fn ty_auto_deref_stability<'tcx>( continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), + ty::Alias(ty::Inherent, _) => unreachable!("inherent projection should have been normalized away above"), ty::Alias(ty::Projection, _) if ty.has_non_region_param() => { TyPosition::new_deref_stable_for_result(precedence, ty) }, diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index a1f675a3b40f6..bf8a64acf08d6 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -273,7 +273,9 @@ impl<'a> Validator<'a> { Type::QualifiedPath { name: _, args, self_type, trait_ } => { self.check_generic_args(&**args); self.check_type(&**self_type); - self.check_path(trait_, PathKind::Trait); + if let Some(trait_) = trait_ { + self.check_path(trait_, PathKind::Trait); + } } } } diff --git a/tests/rustdoc/inherent-projections.rs b/tests/rustdoc/inherent-projections.rs new file mode 100644 index 0000000000000..9bda0acaf83fe --- /dev/null +++ b/tests/rustdoc/inherent-projections.rs @@ -0,0 +1,44 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// @has 'inherent_projections/fn.create.html' +// @has - '//pre[@class="rust item-decl"]' "create() -> Owner::Metadata" +// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Owner.html#associatedtype.Metadata' +pub fn create() -> Owner::Metadata {} + +pub struct Owner; + +impl Owner { + pub type Metadata = (); +} + +// Make sure we handle bound vars correctly. +// @has 'inherent_projections/type.User.html' '//pre[@class="rust item-decl"]' "for<'a> fn(_: Carrier<'a>::Focus)" +pub type User = for<'a> fn(Carrier<'a>::Focus); + +pub struct Carrier<'a>(&'a ()); + +impl<'a> Carrier<'a> { + pub type Focus = &'a mut i32; +} + +//////////////////////////////////////// + +// FIXME(inherent_associated_types): Below we link to `Proj` but we should link to `Proj-1`. +// The current test checks for the buggy behavior for demonstration purposes. + +// @has 'inherent_projections/type.Test.html' +// @has - '//pre[@class="rust item-decl"]' "Parametrized" +// @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj' +// @!has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj-1' +pub type Test = Parametrized::Proj; + +pub struct Parametrized(T); + +impl Parametrized { + pub type Proj = (); +} + +impl Parametrized { + pub type Proj = String; +} diff --git a/tests/rustdoc/intra-doc/inherent-associated-types.rs b/tests/rustdoc/intra-doc/inherent-associated-types.rs new file mode 100644 index 0000000000000..2b28d2ae60bd4 --- /dev/null +++ b/tests/rustdoc/intra-doc/inherent-associated-types.rs @@ -0,0 +1,45 @@ +#![feature(inherent_associated_types)] + +#![allow(incomplete_features)] +#![deny(rustdoc::broken_intra_doc_links)] + +// @has inherent_associated_types/index.html + +// @has - '//a/@href' 'enum.Simple.html#associatedtype.Type' +//! [`Simple::Type`] + +pub enum Simple {} + +impl Simple { + pub type Type = (); +} + +//////////////////////////////////////// + +// @has 'inherent_associated_types/type.Test0.html' '//a/@href' \ +// 'struct.Parametrized.html#associatedtype.Proj' +/// [`Parametrized::Proj`] +pub type Test0 = (); + +// FIXME(inherent_associated_types): The intra-doc link below should point to `Proj-1` not `Proj`. +// The current test checks for the buggy behavior for demonstration purposes. +// The same bug happens for inherent associated functions and constants (see #85960, #93398). +// +// Further, at some point we should reject the intra-doc link `Parametrized::Proj`. +// It currently links to `Parametrized::Proj`. + +// @has 'inherent_associated_types/type.Test1.html' +// @has - '//a/@href' 'struct.Parametrized.html#associatedtype.Proj' +// @!has - '//a/@href' 'struct.Parametrized.html#associatedtype.Proj-1' +/// [`Parametrized::Proj`] +pub type Test1 = (); + +pub struct Parametrized(T); + +impl Parametrized { + pub type Proj = (); +} + +impl Parametrized { + pub type Proj = String; +} diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs new file mode 100644 index 0000000000000..f41574403d887 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs @@ -0,0 +1,10 @@ +// known-bug: #108491 + +// FIXME(inherent_associated_types): This should pass. + +struct Foo { + bar: Self::Bar, +} +impl Foo { + pub type Bar = usize; +} diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr new file mode 100644 index 0000000000000..f313c4946714c --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr @@ -0,0 +1,49 @@ +error[E0601]: `main` function not found in crate `cycle_iat_inside_of_adt` + --> $DIR/cycle-iat-inside-of-adt.rs:10:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/cycle-iat-inside-of-adt.rs` + +error[E0391]: cycle detected when computing predicates of `Foo` + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ + | +note: ...which requires computing predicates of `Foo`... + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ +note: ...which requires computing inferred outlives predicates of `Foo`... + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ + = note: ...which requires computing the inferred outlives predicates for items in this crate... +note: ...which requires computing type of `Foo::bar`... + --> $DIR/cycle-iat-inside-of-adt.rs:6:5 + | +LL | bar: Self::Bar, + | ^^^^^^^^^^^^^^ +note: ...which requires computing normalized predicates of `Foo`... + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | struct Foo { + | ^^^^^^^^^^ + = note: ...which again requires computing predicates of `Foo`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/cycle-iat-inside-of-adt.rs:5:1 + | +LL | / struct Foo { +LL | | bar: Self::Bar, +LL | | } +LL | | impl Foo { +LL | | pub type Bar = usize; +LL | | } + | |_^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0391, E0601. +For more information about an error, try `rustc --explain E0391`. diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs new file mode 100644 index 0000000000000..0c2a38b1173d9 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs @@ -0,0 +1,16 @@ +// known-bug: unknown + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(inherent_associated_types): This shouldn't lead to a cycle error. + +fn user() where S::P: std::fmt::Debug {} + +struct S; + +impl S { + type P = (); +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr new file mode 100644 index 0000000000000..aaa9a39ea0f4a --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr @@ -0,0 +1,37 @@ +error[E0391]: cycle detected when computing predicates of `user` + --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1 + | +LL | fn user() where S::P: std::fmt::Debug {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing predicates of `user`... + --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1 + | +LL | fn user() where S::P: std::fmt::Debug {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires computing explicit predicates of `user`... + --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1 + | +LL | fn user() where S::P: std::fmt::Debug {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: ...which requires computing normalized predicates of `user`... + --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1 + | +LL | fn user() where S::P: std::fmt::Debug {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires computing predicates of `user`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/cycle-iat-inside-of-where-predicate.rs:3:1 + | +LL | / #![feature(inherent_associated_types)] +LL | | #![allow(incomplete_features)] +LL | | +LL | | // FIXME(inherent_associated_types): This shouldn't lead to a cycle error. +... | +LL | | +LL | | fn main() {} + | |____________^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs deleted file mode 100644 index 53ac79e0561ba..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs +++ /dev/null @@ -1,23 +0,0 @@ -// 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 deleted file mode 100644 index 1648cfb266b56..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [typeck] type-checking `weird` -#1 [used_trait_imports] finding used_trait_imports `weird` -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 deleted file mode 100644 index a920b412b1a49..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/inference-fail.rs +++ /dev/null @@ -1,15 +0,0 @@ -// 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/lack-of-regionck.rs b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs deleted file mode 100644 index 632dbf3854b2b..0000000000000 --- a/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs +++ /dev/null @@ -1,19 +0,0 @@ -// 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/bugs/wf-check-skipped.rs b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs new file mode 100644 index 0000000000000..c7f66e645bb57 --- /dev/null +++ b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs @@ -0,0 +1,15 @@ +// known-bug: #100041 +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(inherent_associated_types): This should fail. + +struct Foo; + +impl Foo { + type Bar = (); +} + +fn main() -> Foo::Bar::> {} 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 f846bfa4168ac..83be4f43b5e94 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::Result = vec![true]; + let _: Choose<&str>::Result = vec!["…"]; // regression test for issue #108957 } // Test if we use the correct `ParamEnv` when proving obligations. diff --git a/tests/ui/associated-inherent-types/former-subst-ice.rs b/tests/ui/associated-inherent-types/former-subst-ice.rs new file mode 100644 index 0000000000000..48390b9430b60 --- /dev/null +++ b/tests/ui/associated-inherent-types/former-subst-ice.rs @@ -0,0 +1,16 @@ +// check-pass + +#![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/generic-associated-types-bad.item.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr new file mode 100644 index 0000000000000..464b59c249fe1 --- /dev/null +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/generic-associated-types-bad.rs:16:10 + | +LL | const _: Ty::Pr = String::new(); + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `Ty::Pr` + --> $DIR/generic-associated-types-bad.rs:10:16 + | +LL | type Pr = T; + | ^^^^ required by this bound in `Ty::Pr` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr new file mode 100644 index 0000000000000..4f371b24e8034 --- /dev/null +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `Vec<()>: Copy` is not satisfied + --> $DIR/generic-associated-types-bad.rs:20:12 + | +LL | let _: Ty::Pr>; + | ^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Vec<()>` + | +note: required by a bound in `Ty::Pr` + --> $DIR/generic-associated-types-bad.rs:10:16 + | +LL | type Pr = T; + | ^^^^ required by this bound in `Ty::Pr` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr new file mode 100644 index 0000000000000..74ec39424edcb --- /dev/null +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/generic-associated-types-bad.rs:25:12 + | +LL | fn user<'a>() { + | -- lifetime `'a` defined here +LL | #[cfg(region)] +LL | let _: Ty::Static<&'a str> = ""; + | ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.rs b/tests/ui/associated-inherent-types/generic-associated-types-bad.rs new file mode 100644 index 0000000000000..e66392a0a9411 --- /dev/null +++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.rs @@ -0,0 +1,26 @@ +// revisions: item local region + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +#[derive(Clone, Copy)] +pub enum Ty {} + +impl Ty { + type Pr = T; + + type Static = Q; +} + +#[cfg(item)] +const _: Ty::Pr = String::new(); //[item]~ the trait bound `String: Copy` is not satisfied + +fn main() { + #[cfg(local)] + let _: Ty::Pr>; //[local]~ ERROR the trait bound `Vec<()>: Copy` is not satisfied +} + +fn user<'a>() { + #[cfg(region)] + let _: Ty::Static<&'a str> = ""; //[region]~ ERROR lifetime may not live long enough +} diff --git a/tests/ui/associated-inherent-types/inference-fail.rs b/tests/ui/associated-inherent-types/inference-fail.rs new file mode 100644 index 0000000000000..939a4ff60f2bb --- /dev/null +++ b/tests/ui/associated-inherent-types/inference-fail.rs @@ -0,0 +1,11 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S { type P = (); } + +fn main() { + // There is no way to infer this type. + let _: S<_>::P = (); //~ ERROR type annotations needed +} diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.stderr b/tests/ui/associated-inherent-types/inference-fail.stderr similarity index 53% rename from tests/ui/associated-inherent-types/bugs/inference-fail.stderr rename to tests/ui/associated-inherent-types/inference-fail.stderr index 425691bd6c4f6..f29144e4aa755 100644 --- a/tests/ui/associated-inherent-types/bugs/inference-fail.stderr +++ b/tests/ui/associated-inherent-types/inference-fail.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/inference-fail.rs:14:14 + --> $DIR/inference-fail.rs:10:12 | -LL | let _: S<_>::P; - | ^ cannot infer type +LL | let _: S<_>::P = (); + | ^^^^^^^ cannot infer type for type parameter `T` error: aborting due to previous error diff --git a/tests/ui/associated-inherent-types/inference.rs b/tests/ui/associated-inherent-types/inference.rs new file mode 100644 index 0000000000000..38179214fa124 --- /dev/null +++ b/tests/ui/associated-inherent-types/inference.rs @@ -0,0 +1,39 @@ +// Testing inference capabilities. +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +use std::convert::identity; + +struct Container(T); + +impl Container { + type Sink = (); +} + +impl Container { + type Thing = Any; +} + +impl Container<(T, ())> { + type Output = ((), Wrapped); +} + +fn main() { + // Inferred via the Self type of the impl. + let _: Container<_>::Sink; + + // Inferred via the RHS: + + let _: Container<_>::Thing = 0; + + let _: Container>::Thing = Wrapped(false); + + let _: Container<_>::Output = (drop(1), Wrapped("...")); + + let binding: Container<_>::Thing = Default::default(); // unsolved at this point + identity::(binding); // constrained and solved here +} + +struct Wrapped(T); diff --git a/tests/ui/associated-inherent-types/issue-109768.rs b/tests/ui/associated-inherent-types/issue-109768.rs new file mode 100644 index 0000000000000..a3ae2e2ab4476 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109768.rs @@ -0,0 +1,12 @@ +// incremental + +struct Wrapper(T); + +struct Local(T, U); + +impl Local { //~ ERROR missing generics for struct `Local` + type AssocType3 = T; //~ ERROR inherent associated types are unstable + + const WRAPPED_ASSOC_3: Wrapper = Wrapper(); +} +//~^ ERROR `main` function not found diff --git a/tests/ui/associated-inherent-types/issue-109768.stderr b/tests/ui/associated-inherent-types/issue-109768.stderr new file mode 100644 index 0000000000000..97706d4062a97 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109768.stderr @@ -0,0 +1,35 @@ +error[E0601]: `main` function not found in crate `issue_109768` + --> $DIR/issue-109768.rs:11:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/issue-109768.rs` + +error[E0107]: missing generics for struct `Local` + --> $DIR/issue-109768.rs:7:9 + | +LL | impl Local { + | ^^^^^ expected 2 generic arguments + | +note: struct defined here, with 2 generic parameters: `T`, `U` + --> $DIR/issue-109768.rs:5:8 + | +LL | struct Local(T, U); + | ^^^^^ - - +help: add missing generic arguments + | +LL | impl Local { + | ++++++ + +error[E0658]: inherent associated types are unstable + --> $DIR/issue-109768.rs:8:5 + | +LL | type AssocType3 = T; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #8995 for more information + = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0601, E0658. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/associated-inherent-types/issue-109789.rs b/tests/ui/associated-inherent-types/issue-109789.rs new file mode 100644 index 0000000000000..0b5ba7d1fb55d --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109789.rs @@ -0,0 +1,22 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Foo(T); + +impl Foo { + type Assoc = u32; +} + +trait Other {} +impl Other for u32 {} + +// FIXME(inherent_associated_types): Avoid emitting two diagnostics (they only differ in span). +// FIXME(inherent_associated_types): Enhancement: Spruce up the diagnostic by saying something like +// "implementation is not general enough" as is done for traits via +// `try_report_trait_placeholder_mismatch`. + +fn bar(_: Foo fn(&'a ())>::Assoc) {} +//~^ ERROR mismatched types +//~| ERROR mismatched types + +fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109789.stderr b/tests/ui/associated-inherent-types/issue-109789.stderr new file mode 100644 index 0000000000000..7af338274a12b --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109789.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/issue-109789.rs:18:1 + | +LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected struct `Foo` + found struct `Foo fn(&'a ())>` + +error[E0308]: mismatched types + --> $DIR/issue-109789.rs:18:11 + | +LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected struct `Foo` + found struct `Foo fn(&'a ())>` + +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/issue-109790.rs b/tests/ui/associated-inherent-types/issue-109790.rs new file mode 100644 index 0000000000000..b2be19a28f442 --- /dev/null +++ b/tests/ui/associated-inherent-types/issue-109790.rs @@ -0,0 +1,17 @@ +// check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Foo(T); + +impl<'a> Foo { + type Assoc = &'a (); +} + +trait Other {} +impl Other for u32 {} + +fn bar(_: for<'a> fn(Foo::Assoc)) {} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/late-bound-regions.rs b/tests/ui/associated-inherent-types/late-bound-regions.rs new file mode 100644 index 0000000000000..488a2cda649ce --- /dev/null +++ b/tests/ui/associated-inherent-types/late-bound-regions.rs @@ -0,0 +1,25 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Test if we correctly normalize `S<'a>::P` with respect to late-bound regions. + +type Function = for<'a> fn(&'a i32) -> S<'a>::P; + +struct S<'a>(&'a ()); + +trait Inter { + type P; +} + +impl<'a> S<'a> { + type P = &'a i32; +} + +fn ret_ref_local<'e>() -> &'e i32 { + let f: Function = |x| x; + + let local = 0; + f(&local) //~ ERROR cannot return value referencing local variable `local` +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/late-bound-regions.stderr b/tests/ui/associated-inherent-types/late-bound-regions.stderr new file mode 100644 index 0000000000000..4706fcca91d06 --- /dev/null +++ b/tests/ui/associated-inherent-types/late-bound-regions.stderr @@ -0,0 +1,12 @@ +error[E0515]: cannot return value referencing local variable `local` + --> $DIR/late-bound-regions.rs:22:5 + | +LL | f(&local) + | ^^------^ + | | | + | | `local` is borrowed here + | returns a value referencing data owned by the current function + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/associated-inherent-types/normalization-overflow.rs b/tests/ui/associated-inherent-types/normalization-overflow.rs new file mode 100644 index 0000000000000..4228238aa7b7a --- /dev/null +++ b/tests/ui/associated-inherent-types/normalization-overflow.rs @@ -0,0 +1,12 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(fmease): I'd prefer to report a cycle error here instead of an overflow one. + +struct T; + +impl T { + type This = Self::This; //~ ERROR overflow evaluating associated type `T::This` +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/normalization-overflow.stderr b/tests/ui/associated-inherent-types/normalization-overflow.stderr new file mode 100644 index 0000000000000..16bb64281e3af --- /dev/null +++ b/tests/ui/associated-inherent-types/normalization-overflow.stderr @@ -0,0 +1,8 @@ +error: overflow evaluating associated type `T::This` + --> $DIR/normalization-overflow.rs:9:17 + | +LL | type This = Self::This; + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/associated-inherent-types/private-in-public.rs b/tests/ui/associated-inherent-types/private-in-public.rs new file mode 100644 index 0000000000000..a4b372537c789 --- /dev/null +++ b/tests/ui/associated-inherent-types/private-in-public.rs @@ -0,0 +1,26 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] +#![crate_type = "lib"] + +#![deny(private_in_public)] + +pub type PubAlias0 = PubTy::PrivAssocTy; +//~^ ERROR private associated type `PubTy::PrivAssocTy` in public interface (error E0446) +//~| WARNING this was previously accepted +pub type PubAlias1 = PrivTy::PubAssocTy; +//~^ ERROR private type `PrivTy` in public interface (error E0446) +//~| WARNING this was previously accepted +pub type PubAlias2 = PubTy::PubAssocTy; +//~^ ERROR private type `PrivTy` in public interface (error E0446) +//~| WARNING this was previously accepted + +pub struct PubTy; +impl PubTy { + type PrivAssocTy = (); + pub type PubAssocTy = T; +} + +struct PrivTy; +impl PrivTy { + pub type PubAssocTy = (); +} diff --git a/tests/ui/associated-inherent-types/private-in-public.stderr b/tests/ui/associated-inherent-types/private-in-public.stderr new file mode 100644 index 0000000000000..f0a64e96179ac --- /dev/null +++ b/tests/ui/associated-inherent-types/private-in-public.stderr @@ -0,0 +1,34 @@ +error: private associated type `PubTy::PrivAssocTy` in public interface (error E0446) + --> $DIR/private-in-public.rs:7:1 + | +LL | pub type PubAlias0 = PubTy::PrivAssocTy; + | ^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 +note: the lint level is defined here + --> $DIR/private-in-public.rs:5:9 + | +LL | #![deny(private_in_public)] + | ^^^^^^^^^^^^^^^^^ + +error: private type `PrivTy` in public interface (error E0446) + --> $DIR/private-in-public.rs:10:1 + | +LL | pub type PubAlias1 = PrivTy::PubAssocTy; + | ^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 + +error: private type `PrivTy` in public interface (error E0446) + --> $DIR/private-in-public.rs:13:1 + | +LL | pub type PubAlias2 = PubTy::PubAssocTy; + | ^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #34537 + +error: aborting due to 3 previous errors + diff --git a/tests/ui/associated-inherent-types/regionck-0.rs b/tests/ui/associated-inherent-types/regionck-0.rs new file mode 100644 index 0000000000000..7c94539ace1f9 --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-0.rs @@ -0,0 +1,14 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S<&'static ()> { + type T = (); +} + +fn user<'a>() { + let _: S::<&'a ()>::T; //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/regionck-0.stderr b/tests/ui/associated-inherent-types/regionck-0.stderr new file mode 100644 index 0000000000000..3a438ee630e1e --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-0.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/regionck-0.rs:11:12 + | +LL | fn user<'a>() { + | -- lifetime `'a` defined here +LL | let _: S::<&'a ()>::T; + | ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to previous error + diff --git a/tests/ui/associated-inherent-types/regionck-1.rs b/tests/ui/associated-inherent-types/regionck-1.rs new file mode 100644 index 0000000000000..ec663cd0f77fa --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-1.rs @@ -0,0 +1,13 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct U; + +impl U { + // Don't imply any bounds here. + + type NoTyOutliv<'a, T> = &'a T; //~ ERROR the parameter type `T` may not live long enough + type NoReOutliv<'a, 'b> = &'a &'b (); //~ ERROR reference has a longer lifetime than the data it references +} + +fn main() {} diff --git a/tests/ui/associated-inherent-types/regionck-1.stderr b/tests/ui/associated-inherent-types/regionck-1.stderr new file mode 100644 index 0000000000000..b17d89ca306f4 --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-1.stderr @@ -0,0 +1,29 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/regionck-1.rs:9:30 + | +LL | type NoTyOutliv<'a, T> = &'a T; + | ^^^^^- help: consider adding a where clause: `where T: 'a` + | | + | ...so that the reference type `&'a T` does not outlive the data it points at + +error[E0491]: in type `&'a &'b ()`, reference has a longer lifetime than the data it references + --> $DIR/regionck-1.rs:10:31 + | +LL | type NoReOutliv<'a, 'b> = &'a &'b (); + | ^^^^^^^^^^ + | +note: the pointer is valid for the lifetime `'a` as defined here + --> $DIR/regionck-1.rs:10:21 + | +LL | type NoReOutliv<'a, 'b> = &'a &'b (); + | ^^ +note: but the referenced data is only valid for the lifetime `'b` as defined here + --> $DIR/regionck-1.rs:10:25 + | +LL | type NoReOutliv<'a, 'b> = &'a &'b (); + | ^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0309, E0491. +For more information about an error, try `rustc --explain E0309`. diff --git a/tests/ui/associated-inherent-types/regionck-2.rs b/tests/ui/associated-inherent-types/regionck-2.rs new file mode 100644 index 0000000000000..7a0b8b0830015 --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-2.rs @@ -0,0 +1,14 @@ +// Regression test for issue #109299. + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct Lexer<'d>(&'d ()); + +impl Lexer<'static> { + type Cursor = (); +} + +fn test(_: Lexer::Cursor) {} //~ ERROR mismatched types + +fn main() {} diff --git a/tests/ui/associated-inherent-types/regionck-2.stderr b/tests/ui/associated-inherent-types/regionck-2.stderr new file mode 100644 index 0000000000000..b0a4ed35d5609 --- /dev/null +++ b/tests/ui/associated-inherent-types/regionck-2.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/regionck-2.rs:12:12 + | +LL | fn test(_: Lexer::Cursor) {} + | ^^^^^^^^^^^^^ lifetime mismatch + | + = note: expected struct `Lexer<'static>` + found struct `Lexer<'_>` +note: the anonymous lifetime defined here... + --> $DIR/regionck-2.rs:12:12 + | +LL | fn test(_: Lexer::Cursor) {} + | ^^^^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs new file mode 100644 index 0000000000000..b32b4288ac9f6 --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs @@ -0,0 +1,26 @@ +// check-pass +// compile-flags: --crate-type=lib + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// Bounds on the self type play a major role in the resolution of inherent associated types (*). +// As a result of that, if a type alias contains any then its bounds have to be respected and the +// lint `type_alias_bounds` should not fire. +// +// FIXME(inherent_associated_types): In the current implementation that is. We might move the +// selection phase of IATs from hir_typeck to trait_selection resulting in us not requiring the +// ParamEnv that early allowing us to ignore bounds on type aliases again. +// Triage this before stabilization. + +#![deny(type_alias_bounds)] + +pub type Alias = (Source::Assoc,); + + +pub struct Source(T); +pub trait Bound {} + +impl Source { + pub type Assoc = (); +} diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs new file mode 100644 index 0000000000000..d081c4d5b78e6 --- /dev/null +++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs @@ -0,0 +1,12 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S { + type T = T; +} + +fn main() { + let _: S<_>::T = String::new(); //~ ERROR the trait bound `String: Copy` is not satisfied +} diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr new file mode 100644 index 0000000000000..ecf30f4cdec58 --- /dev/null +++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/unsatisfied-bounds-inferred-type.rs:11:12 + | +LL | let _: S<_>::T = String::new(); + | ^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `S::T` + --> $DIR/unsatisfied-bounds-inferred-type.rs:6:9 + | +LL | impl S { + | ^^^^ required by this bound in `S::T` +LL | type T = T; + | - required by a bound in this associated type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs new file mode 100644 index 0000000000000..97bd2c421604a --- /dev/null +++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs @@ -0,0 +1,14 @@ +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +struct S(T); + +impl S { + type X = () + where + T: Copy; +} + +fn main() { + let _: S::::X; //~ ERROR the trait bound `String: Copy` is not satisfied +} diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr new file mode 100644 index 0000000000000..d4968cd05dcee --- /dev/null +++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/unsatisfied-bounds-where-clause-on-assoc-ty.rs:13:12 + | +LL | let _: S::::X; + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `S::X` + --> $DIR/unsatisfied-bounds-where-clause-on-assoc-ty.rs:9:12 + | +LL | type X = () + | - required by a bound in this associated type +LL | where +LL | T: Copy; + | ^^^^ required by this bound in `S::X` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.