Rollup merge of #121344 - fmease:lta-constr-by-input, r=oli-obk

Expand weak alias types before collecting constrained/referenced late bound regions + refactorings

Fixes #114220.
Follow-up to #120780.

r? `@oli-obk`
This commit is contained in:
Matthias Krüger 2024-02-20 19:35:41 +01:00 committed by GitHub
commit 532b3eacb7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 300 additions and 173 deletions

View file

@ -454,9 +454,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad // for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok // for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
let late_bound_in_projection_ty = let late_bound_in_projection_ty =
tcx.collect_constrained_late_bound_regions(&projection_ty); tcx.collect_constrained_late_bound_regions(projection_ty);
let late_bound_in_term = let late_bound_in_term =
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(term)); tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term));
debug!(?late_bound_in_projection_ty); debug!(?late_bound_in_projection_ty);
debug!(?late_bound_in_term); debug!(?late_bound_in_term);

View file

@ -2678,9 +2678,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// for<'a> fn(&'a String) -> &'a str <-- 'a is ok // for<'a> fn(&'a String) -> &'a str <-- 'a is ok
let inputs = bare_fn_ty.inputs(); let inputs = bare_fn_ty.inputs();
let late_bound_in_args = let late_bound_in_args =
tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned())); tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned()));
let output = bare_fn_ty.output(); let output = bare_fn_ty.output();
let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output); let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output);
self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
struct_span_code_err!( struct_span_code_err!(

View file

@ -144,7 +144,7 @@ impl<'tcx> InherentCollect<'tcx> {
let id = id.owner_id.def_id; let id = id.owner_id.def_id;
let item_span = self.tcx.def_span(id); let item_span = self.tcx.def_span(id);
let self_ty = self.tcx.type_of(id).instantiate_identity(); let self_ty = self.tcx.type_of(id).instantiate_identity();
let self_ty = peel_off_weak_aliases(self.tcx, self_ty); let self_ty = self.tcx.peel_off_weak_alias_tys(self_ty);
match *self_ty.kind() { match *self_ty.kind() {
ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()),
ty::Foreign(did) => self.check_def_id(id, self_ty, did), ty::Foreign(did) => self.check_def_id(id, self_ty, did),
@ -186,30 +186,3 @@ impl<'tcx> InherentCollect<'tcx> {
} }
} }
} }
/// Peel off all weak alias types in this type until there are none left.
///
/// <div class="warning">
///
/// This assumes that `ty` gets normalized later and that any overflows occurring
/// during said normalization get reported.
///
/// </div>
fn peel_off_weak_aliases<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> {
let ty::Alias(ty::Weak, _) = ty.kind() else { return ty };
let limit = tcx.recursion_limit();
let mut depth = 0;
while let ty::Alias(ty::Weak, alias) = ty.kind() {
if !limit.value_within_limit(depth) {
let guar = tcx.dcx().delayed_bug("overflow expanding weak alias type");
return Ty::new_error(tcx, guar);
}
ty = tcx.type_of(alias.def_id).instantiate(tcx, alias.args);
depth += 1;
}
ty
}

View file

@ -520,7 +520,7 @@ fn get_new_lifetime_name<'tcx>(
generics: &hir::Generics<'tcx>, generics: &hir::Generics<'tcx>,
) -> String { ) -> String {
let existing_lifetimes = tcx let existing_lifetimes = tcx
.collect_referenced_late_bound_regions(&poly_trait_ref) .collect_referenced_late_bound_regions(poly_trait_ref)
.into_iter() .into_iter()
.filter_map(|lt| { .filter_map(|lt| {
if let ty::BoundRegionKind::BrNamed(_, name) = lt { if let ty::BoundRegionKind::BrNamed(_, name) = lt {

View file

@ -1,8 +1,8 @@
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span; use rustc_span::Span;
use rustc_type_ir::fold::TypeFoldable;
use std::ops::ControlFlow; use std::ops::ControlFlow;
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
@ -33,62 +33,47 @@ pub fn parameters_for_impl<'tcx>(
impl_trait_ref: Option<ty::TraitRef<'tcx>>, impl_trait_ref: Option<ty::TraitRef<'tcx>>,
) -> FxHashSet<Parameter> { ) -> FxHashSet<Parameter> {
let vec = match impl_trait_ref { let vec = match impl_trait_ref {
Some(tr) => parameters_for(tcx, &tr, false), Some(tr) => parameters_for(tcx, tr, false),
None => parameters_for(tcx, &impl_self_ty, false), None => parameters_for(tcx, impl_self_ty, false),
}; };
vec.into_iter().collect() vec.into_iter().collect()
} }
/// If `include_nonconstraining` is false, returns the list of parameters that are /// If `include_nonconstraining` is false, returns the list of parameters that are
/// constrained by `t` - i.e., the value of each parameter in the list is /// constrained by `value` - i.e., the value of each parameter in the list is
/// uniquely determined by `t` (see RFC 447). If it is true, return the list /// uniquely determined by `value` (see RFC 447). If it is true, return the list
/// of parameters whose values are needed in order to constrain `ty` - these /// of parameters whose values are needed in order to constrain `value` - these
/// differ, with the latter being a superset, in the presence of projections. /// differ, with the latter being a superset, in the presence of projections.
pub fn parameters_for<'tcx>( pub fn parameters_for<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
t: &impl TypeVisitable<TyCtxt<'tcx>>, value: impl TypeFoldable<TyCtxt<'tcx>>,
include_nonconstraining: bool, include_nonconstraining: bool,
) -> Vec<Parameter> { ) -> Vec<Parameter> {
let mut collector = let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining };
ParameterCollector { tcx, parameters: vec![], include_nonconstraining, depth: 0 }; let value = if !include_nonconstraining { tcx.expand_weak_alias_tys(value) } else { value };
t.visit_with(&mut collector); value.visit_with(&mut collector);
collector.parameters collector.parameters
} }
struct ParameterCollector<'tcx> { struct ParameterCollector {
tcx: TyCtxt<'tcx>,
parameters: Vec<Parameter>, parameters: Vec<Parameter>,
include_nonconstraining: bool, include_nonconstraining: bool,
depth: usize,
} }
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector<'tcx> { impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match *t.kind() { match *t.kind() {
// Projections are not injective in general.
ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _)
if !self.include_nonconstraining => if !self.include_nonconstraining =>
{ {
// Projections are not injective in general.
return ControlFlow::Continue(()); return ControlFlow::Continue(());
} }
ty::Alias(ty::Weak, alias) if !self.include_nonconstraining => { // All weak alias types should've been expanded beforehand.
if !self.tcx.recursion_limit().value_within_limit(self.depth) { ty::Alias(ty::Weak, _) if !self.include_nonconstraining => {
// Other constituent types may still constrain some generic params, consider bug!("unexpected weak alias type")
// `<T> (Overflow, T)` for example. Therefore we want to continue instead of
// breaking. Only affects diagnostics.
return ControlFlow::Continue(());
}
self.depth += 1;
return ensure_sufficient_stack(|| {
self.tcx
.type_of(alias.def_id)
.instantiate(self.tcx, alias.args)
.visit_with(self)
});
}
ty::Param(data) => {
self.parameters.push(Parameter::from(data));
} }
ty::Param(param) => self.parameters.push(Parameter::from(param)),
_ => {} _ => {}
} }
@ -224,12 +209,12 @@ pub fn setup_constraining_predicates<'tcx>(
// `<<T as Bar>::Baz as Iterator>::Output = <U as Iterator>::Output` // `<<T as Bar>::Baz as Iterator>::Output = <U as Iterator>::Output`
// Then the projection only applies if `T` is known, but it still // Then the projection only applies if `T` is known, but it still
// does not determine `U`. // does not determine `U`.
let inputs = parameters_for(tcx, &projection.projection_ty, true); let inputs = parameters_for(tcx, projection.projection_ty, true);
let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p)); let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p));
if !relies_only_on_inputs { if !relies_only_on_inputs {
continue; continue;
} }
input_parameters.extend(parameters_for(tcx, &projection.term, false)); input_parameters.extend(parameters_for(tcx, projection.term, false));
} else { } else {
continue; continue;
} }

View file

@ -111,7 +111,7 @@ fn enforce_impl_params_are_constrained(
match item.kind { match item.kind {
ty::AssocKind::Type => { ty::AssocKind::Type => {
if item.defaultness(tcx).has_value() { if item.defaultness(tcx).has_value() {
cgp::parameters_for(tcx, &tcx.type_of(def_id).instantiate_identity(), true) cgp::parameters_for(tcx, tcx.type_of(def_id).instantiate_identity(), true)
} else { } else {
vec![] vec![]
} }

View file

@ -133,7 +133,7 @@ fn check_always_applicable(
res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span)); res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span));
res = res.and(check_static_lifetimes(tcx, &parent_args, span)); res = res.and(check_static_lifetimes(tcx, &parent_args, span));
res = res.and(check_duplicate_params(tcx, impl1_args, &parent_args, span)); res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span));
res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span)); res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
res res
@ -266,15 +266,15 @@ fn unconstrained_parent_impl_args<'tcx>(
continue; continue;
} }
unconstrained_parameters.extend(cgp::parameters_for(tcx, &projection_ty, true)); unconstrained_parameters.extend(cgp::parameters_for(tcx, projection_ty, true));
for param in cgp::parameters_for(tcx, &projected_ty, false) { for param in cgp::parameters_for(tcx, projected_ty, false) {
if !unconstrained_parameters.contains(&param) { if !unconstrained_parameters.contains(&param) {
constrained_params.insert(param.0); constrained_params.insert(param.0);
} }
} }
unconstrained_parameters.extend(cgp::parameters_for(tcx, &projected_ty, true)); unconstrained_parameters.extend(cgp::parameters_for(tcx, projected_ty, true));
} }
} }
@ -309,7 +309,7 @@ fn unconstrained_parent_impl_args<'tcx>(
fn check_duplicate_params<'tcx>( fn check_duplicate_params<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
impl1_args: GenericArgsRef<'tcx>, impl1_args: GenericArgsRef<'tcx>,
parent_args: &Vec<GenericArg<'tcx>>, parent_args: Vec<GenericArg<'tcx>>,
span: Span, span: Span,
) -> Result<(), ErrorGuaranteed> { ) -> Result<(), ErrorGuaranteed> {
let mut base_params = cgp::parameters_for(tcx, parent_args, true); let mut base_params = cgp::parameters_for(tcx, parent_args, true);

View file

@ -5,7 +5,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::TyCtxt; use crate::infer::TyCtxt;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{self, Binder, Region, Ty, TypeVisitable}; use rustc_middle::ty::{self, Binder, Region, Ty, TypeFoldable};
use rustc_span::Span; use rustc_span::Span;
/// Information about the anonymous region we are searching for. /// Information about the anonymous region we are searching for.
@ -142,10 +142,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
fn includes_region( fn includes_region(
&self, &self,
ty: Binder<'tcx, impl TypeVisitable<TyCtxt<'tcx>>>, ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>,
region: ty::BoundRegionKind, region: ty::BoundRegionKind,
) -> bool { ) -> bool {
let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty); let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty);
// We are only checking is any region meets the condition so order doesn't matter // We are only checking is any region meets the condition so order doesn't matter
#[allow(rustc::potential_query_instability)] #[allow(rustc::potential_query_instability)]
late_bound_regions.iter().any(|r| *r == region) late_bound_regions.iter().any(|r| *r == region)

View file

@ -47,7 +47,7 @@ macro_rules! arena_types {
rustc_middle::traits::query::DropckOutlivesResult<'tcx> rustc_middle::traits::query::DropckOutlivesResult<'tcx>
> >
>, >,
[] normalize_projection_ty: [] normalize_canonicalized_projection_ty:
rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::Canonical<'tcx,
rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx,
rustc_middle::traits::query::NormalizationResult<'tcx> rustc_middle::traits::query::NormalizationResult<'tcx>

View file

@ -31,7 +31,7 @@ use crate::query::plumbing::{
}; };
use crate::thir; use crate::thir;
use crate::traits::query::{ use crate::traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalAliasGoal, CanonicalPredicateGoal, CanonicalTyGoal,
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution,
}; };
@ -1938,9 +1938,13 @@ rustc_queries! {
arena_cache arena_cache
} }
/// Do not call this query directly: invoke `normalize` instead. /// <div class="warning">
query normalize_projection_ty( ///
goal: CanonicalProjectionGoal<'tcx> /// Do not call this query directly: Invoke `normalize` instead.
///
/// </div>
query normalize_canonicalized_projection_ty(
goal: CanonicalAliasGoal<'tcx>
) -> Result< ) -> Result<
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
NoSolution, NoSolution,
@ -1948,9 +1952,13 @@ rustc_queries! {
desc { "normalizing `{}`", goal.value.value } desc { "normalizing `{}`", goal.value.value }
} }
/// Do not call this query directly: invoke `normalize` instead. /// <div class="warning">
query normalize_weak_ty( ///
goal: CanonicalProjectionGoal<'tcx> /// Do not call this query directly: Invoke `normalize` instead.
///
/// </div>
query normalize_canonicalized_weak_ty(
goal: CanonicalAliasGoal<'tcx>
) -> Result< ) -> Result<
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
NoSolution, NoSolution,
@ -1958,9 +1966,13 @@ rustc_queries! {
desc { "normalizing `{}`", goal.value.value } desc { "normalizing `{}`", goal.value.value }
} }
/// Do not call this query directly: invoke `normalize` instead. /// <div class="warning">
query normalize_inherent_projection_ty( ///
goal: CanonicalProjectionGoal<'tcx> /// Do not call this query directly: Invoke `normalize` instead.
///
/// </div>
query normalize_canonicalized_inherent_projection_ty(
goal: CanonicalAliasGoal<'tcx>
) -> Result< ) -> Result<
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
NoSolution, NoSolution,

View file

@ -67,7 +67,7 @@ pub mod type_op {
} }
} }
pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; pub type CanonicalAliasGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>;
pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>;
@ -177,10 +177,10 @@ pub struct MethodAutoderefBadTy<'tcx> {
pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
} }
/// Result from the `normalize_projection_ty` query. /// Result of the `normalize_canonicalized_{{,inherent_}projection,weak}_ty` queries.
#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] #[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
pub struct NormalizationResult<'tcx> { pub struct NormalizationResult<'tcx> {
/// Result of normalization. /// Result of the normalization.
pub normalized_ty: Ty<'tcx>, pub normalized_ty: Ty<'tcx>,
} }

View file

@ -181,9 +181,10 @@ impl FlagComputation {
&ty::Alias(kind, data) => { &ty::Alias(kind, data) => {
self.add_flags(match kind { self.add_flags(match kind {
ty::Weak | ty::Projection => TypeFlags::HAS_TY_PROJECTION, ty::Projection => TypeFlags::HAS_TY_PROJECTION,
ty::Inherent => TypeFlags::HAS_TY_INHERENT, ty::Weak => TypeFlags::HAS_TY_WEAK,
ty::Opaque => TypeFlags::HAS_TY_OPAQUE, ty::Opaque => TypeFlags::HAS_TY_OPAQUE,
ty::Inherent => TypeFlags::HAS_TY_INHERENT,
}); });
self.add_alias_ty(data); self.add_alias_ty(data);

View file

@ -11,6 +11,7 @@ use crate::ty::{GenericArgKind, GenericArgsRef};
use rustc_apfloat::Float as _; use rustc_apfloat::Float as _;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def::{CtorOf, DefKind, Res};
@ -867,6 +868,63 @@ impl<'tcx> TyCtxt<'tcx> {
self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param)) self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param))
} }
/// Expand any [weak alias types][weak] contained within the given `value`.
///
/// This should be used over other normalization routines in situations where
/// it's important not to normalize other alias types and where the predicates
/// on the corresponding type alias shouldn't be taken into consideration.
///
/// Whenever possible **prefer not to use this function**! Instead, use standard
/// normalization routines or if feasible don't normalize at all.
///
/// This function comes in handy if you want to mimic the behavior of eager
/// type alias expansion in a localized manner.
///
/// <div class="warning">
/// This delays a bug on overflow! Therefore you need to be certain that the
/// contained types get fully normalized at a later stage. Note that even on
/// overflow all well-behaved weak alias types get expanded correctly, so the
/// result is still useful.
/// </div>
///
/// [weak]: ty::Weak
pub fn expand_weak_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, value: T) -> T {
value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 })
}
/// Peel off all [weak alias types] in this type until there are none left.
///
/// This only expands weak alias types in “head” / outermost positions. It can
/// be used over [expand_weak_alias_tys] as an optimization in situations where
/// one only really cares about the *kind* of the final aliased type but not
/// the types the other constituent types alias.
///
/// <div class="warning">
/// This delays a bug on overflow! Therefore you need to be certain that the
/// type gets fully normalized at a later stage.
/// </div>
///
/// [weak]: ty::Weak
/// [expand_weak_alias_tys]: Self::expand_weak_alias_tys
pub fn peel_off_weak_alias_tys(self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
let ty::Alias(ty::Weak, _) = ty.kind() else { return ty };
let limit = self.recursion_limit();
let mut depth = 0;
while let ty::Alias(ty::Weak, alias) = ty.kind() {
if !limit.value_within_limit(depth) {
let guar = self.dcx().delayed_bug("overflow expanding weak alias type");
return Ty::new_error(self, guar);
}
ty = self.type_of(alias.def_id).instantiate(self, alias.args);
depth += 1;
}
ty
}
} }
struct OpaqueTypeExpander<'tcx> { struct OpaqueTypeExpander<'tcx> {
@ -1002,6 +1060,42 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
} }
} }
struct WeakAliasTypeExpander<'tcx> {
tcx: TyCtxt<'tcx>,
depth: usize,
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> {
fn interner(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
return ty;
}
let ty::Alias(ty::Weak, alias) = ty.kind() else {
return ty.super_fold_with(self);
};
if !self.tcx.recursion_limit().value_within_limit(self.depth) {
let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type");
return Ty::new_error(self.tcx, guar);
}
self.depth += 1;
ensure_sufficient_stack(|| {
self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self)
})
}
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
if !ct.ty().has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
return ct;
}
ct.super_fold_with(self)
}
}
impl<'tcx> Ty<'tcx> { impl<'tcx> Ty<'tcx> {
/// Returns the `Size` for primitive types (bool, uint, int, char, float). /// Returns the `Size` for primitive types (bool, uint, int, char, float).
pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size { pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size {

View file

@ -2,6 +2,7 @@ use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::sso::SsoHashSet;
use rustc_type_ir::fold::TypeFoldable;
use std::ops::ControlFlow; use std::ops::ControlFlow;
pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
@ -109,10 +110,10 @@ impl<'tcx> TyCtxt<'tcx> {
/// variables will also be equated. /// variables will also be equated.
pub fn collect_constrained_late_bound_regions<T>( pub fn collect_constrained_late_bound_regions<T>(
self, self,
value: &Binder<'tcx, T>, value: Binder<'tcx, T>,
) -> FxHashSet<ty::BoundRegionKind> ) -> FxHashSet<ty::BoundRegionKind>
where where
T: TypeVisitable<TyCtxt<'tcx>>, T: TypeFoldable<TyCtxt<'tcx>>,
{ {
self.collect_late_bound_regions(value, true) self.collect_late_bound_regions(value, true)
} }
@ -120,24 +121,26 @@ impl<'tcx> TyCtxt<'tcx> {
/// Returns a set of all late-bound regions that appear in `value` anywhere. /// Returns a set of all late-bound regions that appear in `value` anywhere.
pub fn collect_referenced_late_bound_regions<T>( pub fn collect_referenced_late_bound_regions<T>(
self, self,
value: &Binder<'tcx, T>, value: Binder<'tcx, T>,
) -> FxHashSet<ty::BoundRegionKind> ) -> FxHashSet<ty::BoundRegionKind>
where where
T: TypeVisitable<TyCtxt<'tcx>>, T: TypeFoldable<TyCtxt<'tcx>>,
{ {
self.collect_late_bound_regions(value, false) self.collect_late_bound_regions(value, false)
} }
fn collect_late_bound_regions<T>( fn collect_late_bound_regions<T>(
self, self,
value: &Binder<'tcx, T>, value: Binder<'tcx, T>,
just_constraint: bool, just_constrained: bool,
) -> FxHashSet<ty::BoundRegionKind> ) -> FxHashSet<ty::BoundRegionKind>
where where
T: TypeVisitable<TyCtxt<'tcx>>, T: TypeFoldable<TyCtxt<'tcx>>,
{ {
let mut collector = LateBoundRegionsCollector::new(just_constraint); let mut collector = LateBoundRegionsCollector::new(just_constrained);
let result = value.as_ref().skip_binder().visit_with(&mut collector); let value = value.skip_binder();
let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value };
let result = value.visit_with(&mut collector);
assert!(result.is_continue()); // should never have stopped early assert!(result.is_continue()); // should never have stopped early
collector.regions collector.regions
} }
@ -258,11 +261,7 @@ struct LateBoundRegionsCollector {
impl LateBoundRegionsCollector { impl LateBoundRegionsCollector {
fn new(just_constrained: bool) -> Self { fn new(just_constrained: bool) -> Self {
LateBoundRegionsCollector { Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained }
current_index: ty::INNERMOST,
regions: Default::default(),
just_constrained,
}
} }
} }
@ -278,13 +277,17 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
} }
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
// if we are only looking for "constrained" region, we have to
// ignore the inputs to a projection, as they may not appear
// in the normalized form
if self.just_constrained { if self.just_constrained {
if let ty::Alias(..) = t.kind() { match t.kind() {
// If we are only looking for "constrained" regions, we have to ignore the
// inputs to a projection as they may not appear in the normalized form.
ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => {
return ControlFlow::Continue(()); return ControlFlow::Continue(());
} }
// All weak alias types should've been expanded beforehand.
ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"),
_ => {}
}
} }
t.super_visit_with(self) t.super_visit_with(self)

View file

@ -101,19 +101,17 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
value: &T, value: &T,
reveal: Reveal, reveal: Reveal,
) -> bool { ) -> bool {
let mut flags = ty::TypeFlags::HAS_TY_PROJECTION
| ty::TypeFlags::HAS_TY_WEAK
| ty::TypeFlags::HAS_TY_INHERENT
| ty::TypeFlags::HAS_CT_PROJECTION;
match reveal { match reveal {
Reveal::UserFacing => value.has_type_flags( Reveal::UserFacing => {}
ty::TypeFlags::HAS_TY_PROJECTION Reveal::All => flags |= ty::TypeFlags::HAS_TY_OPAQUE,
| 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,
),
} }
value.has_type_flags(flags)
} }
struct AssocTypeNormalizer<'a, 'b, 'tcx> { struct AssocTypeNormalizer<'a, 'b, 'tcx> {
@ -355,8 +353,6 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
let data = data.fold_with(self); let data = data.fold_with(self);
// FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement`
// here like `ty::Projection`?
project::normalize_inherent_projection( project::normalize_inherent_projection(
self.selcx, self.selcx,
self.param_env, self.param_env,

View file

@ -1,6 +1,6 @@
//! Code for the 'normalization' query. This consists of a wrapper //! Code for the 'normalization' query. This consists of a wrapper
//! which folds deeply, invoking the underlying //! which folds deeply, invoking the underlying
//! `normalize_projection_ty` query when it encounters projections. //! `normalize_canonicalized_projection_ty` query when it encounters projections.
use crate::infer::at::At; use crate::infer::at::At;
use crate::infer::canonical::OriginalQueryValues; use crate::infer::canonical::OriginalQueryValues;
@ -271,9 +271,9 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
debug!("QueryNormalizer: c_data = {:#?}", c_data); debug!("QueryNormalizer: c_data = {:#?}", c_data);
debug!("QueryNormalizer: orig_values = {:#?}", orig_values); debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
let result = match kind { let result = match kind {
ty::Projection => tcx.normalize_projection_ty(c_data), ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data),
ty::Weak => tcx.normalize_weak_ty(c_data), ty::Weak => tcx.normalize_canonicalized_weak_ty(c_data),
ty::Inherent => tcx.normalize_inherent_projection_ty(c_data), ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data),
kind => unreachable!("did not expect {kind:?} due to match arm above"), kind => unreachable!("did not expect {kind:?} due to match arm above"),
}?; }?;
// We don't expect ambiguity. // We don't expect ambiguity.
@ -308,10 +308,10 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
} else { } else {
result.normalized_ty result.normalized_ty
}; };
// `tcx.normalize_projection_ty` may normalize to a type that still has // `tcx.normalize_canonicalized_projection_ty` may normalize to a type that
// unevaluated consts, so keep normalizing here if that's the case. // still has unevaluated consts, so keep normalizing here if that's the case.
// Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type // Similarly, `tcx.normalize_canonicalized_weak_ty` will only unwrap one layer
// and we need to continue folding it to reveal the TAIT behind it. // of type and we need to continue folding it to reveal the TAIT behind it.
if res != ty if res != ty
&& (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak) && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak)
{ {

View file

@ -5,7 +5,7 @@ use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::infer::InferCtxtBuilderExt;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::query::{ use rustc_trait_selection::traits::query::{
normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult, CanonicalAliasGoal, NoSolution,
}; };
use rustc_trait_selection::traits::{ use rustc_trait_selection::traits::{
self, FulfillmentErrorCode, ObligationCause, SelectionContext, self, FulfillmentErrorCode, ObligationCause, SelectionContext,
@ -13,18 +13,19 @@ use rustc_trait_selection::traits::{
pub(crate) fn provide(p: &mut Providers) { pub(crate) fn provide(p: &mut Providers) {
*p = Providers { *p = Providers {
normalize_projection_ty, normalize_canonicalized_projection_ty,
normalize_weak_ty, normalize_canonicalized_weak_ty,
normalize_inherent_projection_ty, normalize_canonicalized_inherent_projection_ty,
..*p ..*p
}; };
} }
fn normalize_projection_ty<'tcx>( fn normalize_canonicalized_projection_ty<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
goal: CanonicalProjectionGoal<'tcx>, goal: CanonicalAliasGoal<'tcx>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
debug!("normalize_provider(goal={:#?})", goal); debug!("normalize_canonicalized_projection_ty(goal={:#?})", goal);
tcx.infer_ctxt().enter_canonical_trait_query( tcx.infer_ctxt().enter_canonical_trait_query(
&goal, &goal,
|ocx, ParamEnvAnd { param_env, value: goal }| { |ocx, ParamEnvAnd { param_env, value: goal }| {
@ -61,19 +62,19 @@ fn normalize_projection_ty<'tcx>(
return Err(NoSolution); return Err(NoSolution);
} }
// FIXME(associated_const_equality): All users of normalize_projection_ty expected // FIXME(associated_const_equality): All users of normalize_canonicalized_projection_ty
// a type, but there is the possibility it could've been a const now. Maybe change // expected a type, but there is the possibility it could've been a const now.
// it to a Term later? // Maybe change it to a Term later?
Ok(NormalizationResult { normalized_ty: answer.ty().unwrap() }) Ok(NormalizationResult { normalized_ty: answer.ty().unwrap() })
}, },
) )
} }
fn normalize_weak_ty<'tcx>( fn normalize_canonicalized_weak_ty<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
goal: CanonicalProjectionGoal<'tcx>, goal: CanonicalAliasGoal<'tcx>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
debug!("normalize_provider(goal={:#?})", goal); debug!("normalize_canonicalized_weak_ty(goal={:#?})", goal);
tcx.infer_ctxt().enter_canonical_trait_query( tcx.infer_ctxt().enter_canonical_trait_query(
&goal, &goal,
@ -95,11 +96,11 @@ fn normalize_weak_ty<'tcx>(
) )
} }
fn normalize_inherent_projection_ty<'tcx>( fn normalize_canonicalized_inherent_projection_ty<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
goal: CanonicalProjectionGoal<'tcx>, goal: CanonicalAliasGoal<'tcx>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> { ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
debug!("normalize_provider(goal={:#?})", goal); debug!("normalize_canonicalized_inherent_projection_ty(goal={:#?})", goal);
tcx.infer_ctxt().enter_canonical_trait_query( tcx.infer_ctxt().enter_canonical_trait_query(
&goal, &goal,

View file

@ -69,32 +69,35 @@ bitflags! {
/// Does this have `Projection`? /// Does this have `Projection`?
const HAS_TY_PROJECTION = 1 << 10; const HAS_TY_PROJECTION = 1 << 10;
/// Does this have `Inherent`? /// Does this have `Weak`?
const HAS_TY_INHERENT = 1 << 11; const HAS_TY_WEAK = 1 << 11;
/// Does this have `Opaque`? /// Does this have `Opaque`?
const HAS_TY_OPAQUE = 1 << 12; const HAS_TY_OPAQUE = 1 << 12;
/// Does this have `Inherent`?
const HAS_TY_INHERENT = 1 << 13;
/// Does this have `ConstKind::Unevaluated`? /// Does this have `ConstKind::Unevaluated`?
const HAS_CT_PROJECTION = 1 << 13; const HAS_CT_PROJECTION = 1 << 14;
/// Could this type be normalized further? /// Could this type be normalized further?
const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits() const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits()
| TypeFlags::HAS_TY_WEAK.bits()
| TypeFlags::HAS_TY_OPAQUE.bits() | TypeFlags::HAS_TY_OPAQUE.bits()
| TypeFlags::HAS_TY_INHERENT.bits() | TypeFlags::HAS_TY_INHERENT.bits()
| TypeFlags::HAS_CT_PROJECTION.bits(); | TypeFlags::HAS_CT_PROJECTION.bits();
/// Is an error type/const reachable? /// Is an error type/const reachable?
const HAS_ERROR = 1 << 14; const HAS_ERROR = 1 << 15;
/// Does this have any region that "appears free" in the type? /// Does this have any region that "appears free" in the type?
/// Basically anything but `ReBound` and `ReErased`. /// Basically anything but `ReBound` and `ReErased`.
const HAS_FREE_REGIONS = 1 << 15; const HAS_FREE_REGIONS = 1 << 16;
/// Does this have any `ReBound` regions? /// Does this have any `ReBound` regions?
const HAS_RE_BOUND = 1 << 16; const HAS_RE_BOUND = 1 << 17;
/// Does this have any `Bound` types? /// Does this have any `Bound` types?
const HAS_TY_BOUND = 1 << 17; const HAS_TY_BOUND = 1 << 18;
/// Does this have any `ConstKind::Bound` consts? /// Does this have any `ConstKind::Bound` consts?
const HAS_CT_BOUND = 1 << 18; const HAS_CT_BOUND = 1 << 19;
/// Does this have any bound variables? /// Does this have any bound variables?
/// Used to check if a global bound is safe to evaluate. /// Used to check if a global bound is safe to evaluate.
const HAS_BOUND_VARS = TypeFlags::HAS_RE_BOUND.bits() const HAS_BOUND_VARS = TypeFlags::HAS_RE_BOUND.bits()
@ -102,22 +105,22 @@ bitflags! {
| TypeFlags::HAS_CT_BOUND.bits(); | TypeFlags::HAS_CT_BOUND.bits();
/// Does this have any `ReErased` regions? /// Does this have any `ReErased` regions?
const HAS_RE_ERASED = 1 << 19; const HAS_RE_ERASED = 1 << 20;
/// Does this value have parameters/placeholders/inference variables which could be /// Does this value have parameters/placeholders/inference variables which could be
/// replaced later, in a way that would change the results of `impl` specialization? /// replaced later, in a way that would change the results of `impl` specialization?
const STILL_FURTHER_SPECIALIZABLE = 1 << 20; const STILL_FURTHER_SPECIALIZABLE = 1 << 21;
/// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`?
const HAS_TY_FRESH = 1 << 21; const HAS_TY_FRESH = 1 << 22;
/// Does this value have `InferConst::Fresh`? /// Does this value have `InferConst::Fresh`?
const HAS_CT_FRESH = 1 << 22; const HAS_CT_FRESH = 1 << 23;
/// Does this have `Coroutine` or `CoroutineWitness`? /// Does this have `Coroutine` or `CoroutineWitness`?
const HAS_TY_COROUTINE = 1 << 23; const HAS_TY_COROUTINE = 1 << 24;
/// Does this have any binders with bound vars (e.g. that need to be anonymized)? /// Does this have any binders with bound vars (e.g. that need to be anonymized)?
const HAS_BINDER_VARS = 1 << 24; const HAS_BINDER_VARS = 1 << 25;
} }
} }

View file

@ -318,13 +318,12 @@ where
fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxHashSet<GenericParamDef> { fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxHashSet<GenericParamDef> {
let bound_predicate = pred.kind(); let bound_predicate = pred.kind();
let tcx = self.cx.tcx; let tcx = self.cx.tcx;
let regions = match bound_predicate.skip_binder() { let regions =
ty::ClauseKind::Trait(poly_trait_pred) => { match bound_predicate.skip_binder() {
tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_trait_pred)) ty::ClauseKind::Trait(poly_trait_pred) => tcx
} .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_trait_pred)),
ty::ClauseKind::Projection(poly_proj_pred) => { ty::ClauseKind::Projection(poly_proj_pred) => tcx
tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_proj_pred)) .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_proj_pred)),
}
_ => return FxHashSet::default(), _ => return FxHashSet::default(),
}; };

View file

@ -0,0 +1,15 @@
//@ check-pass
// Weak alias types constrain late-bound regions if their normalized form constrains them.
#![feature(lazy_type_alias)]
#![allow(incomplete_features)]
type Ref<'a> = &'a ();
type FnPtr = for<'a> fn(Ref<'a>) -> &'a (); // OK
type DynCl = dyn for<'a> Fn(Ref<'a>) -> &'a (); // OK
fn map0(_: Ref) -> Ref { &() } // OK
fn map1(_: Ref<'_>) -> Ref<'_> { &() } // OK
fn main() {}

View file

@ -0,0 +1,23 @@
// Weak alias types only constrain late-bound regions if their normalized form constrains them.
#![feature(lazy_type_alias)]
#![allow(incomplete_features)]
type NotInjective<'a> = <() as Discard>::Output<'a>;
type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a ();
//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types
type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>;
//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types
type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a ();
//~^ ERROR references lifetime `'a`, which does not appear in the trait input types
trait Discard { type Output<'a>; }
impl Discard for () { type Output<'_a> = (); }
type NotInjectiveEither<'a, Linchpin> = Linchpin
where
Linchpin: Fn() -> &'a ();
fn main() {}

View file

@ -0,0 +1,22 @@
error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types
--> $DIR/unconstrained-late-bound-regions.rs:8:47
|
LL | type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a ();
| ^^^^^^
error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types
--> $DIR/unconstrained-late-bound-regions.rs:10:57
|
LL | type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types
--> $DIR/unconstrained-late-bound-regions.rs:12:50
|
LL | type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a ();
| ^^^^^^
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0581, E0582.
For more information about an error, try `rustc --explain E0581`.

View file

@ -1,5 +1,5 @@
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
--> $DIR/unconstrained-param-due-to-overflow.rs:4:6 --> $DIR/unconstrained-params-in-impl-due-to-overflow.rs:4:6
| |
LL | impl<T> Loop<T> {} LL | impl<T> Loop<T> {}
| ^ unconstrained type parameter | ^ unconstrained type parameter

View file

@ -1,5 +1,5 @@
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
--> $DIR/unconstrained-params.rs:4:6 --> $DIR/unconstrained-params-in-impl.rs:4:6
| |
LL | impl<T> NotInjective<T> {} LL | impl<T> NotInjective<T> {}
| ^ unconstrained type parameter | ^ unconstrained type parameter