Use the normalizing param-env always in check_type_bounds
This commit is contained in:
parent
b20f40dba9
commit
bb74d7e97d
@ -2162,45 +2162,140 @@ pub(super) fn check_type_bounds<'tcx>(
|
||||
impl_ty: ty::AssocItem,
|
||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let param_env = param_env_with_gat_bounds(tcx, trait_ty, impl_ty, impl_trait_ref);
|
||||
debug!(?param_env);
|
||||
|
||||
let container_id = impl_ty.container_id(tcx);
|
||||
let impl_ty_def_id = impl_ty.def_id.expect_local();
|
||||
let impl_ty_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id);
|
||||
let rebased_args = impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
|
||||
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
|
||||
// A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the
|
||||
// span for an impl's associated type. Instead, for these, use the def_span for the synthesized
|
||||
// associated type.
|
||||
let impl_ty_span = if impl_ty.is_impl_trait_in_trait() {
|
||||
tcx.def_span(impl_ty_def_id)
|
||||
} else {
|
||||
match tcx.hir().get_by_def_id(impl_ty_def_id) {
|
||||
hir::Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Type(_, Some(ty)),
|
||||
..
|
||||
}) => ty.span,
|
||||
hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Type(ty), .. }) => ty.span,
|
||||
_ => bug!(),
|
||||
}
|
||||
};
|
||||
let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl_ty_def_id)?;
|
||||
|
||||
let normalize_cause = ObligationCause::new(
|
||||
impl_ty_span,
|
||||
impl_ty_def_id,
|
||||
ObligationCauseCode::CheckAssociatedTypeBounds {
|
||||
impl_item_def_id: impl_ty.def_id.expect_local(),
|
||||
trait_item_def_id: trait_ty.def_id,
|
||||
},
|
||||
);
|
||||
let mk_cause = |span: Span| {
|
||||
let code = if span.is_dummy() {
|
||||
traits::ItemObligation(trait_ty.def_id)
|
||||
} else {
|
||||
traits::BindingObligation(trait_ty.def_id, span)
|
||||
};
|
||||
ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
|
||||
};
|
||||
|
||||
let obligations: Vec<_> = tcx
|
||||
.explicit_item_bounds(trait_ty.def_id)
|
||||
.iter_instantiated_copied(tcx, rebased_args)
|
||||
.map(|(concrete_ty_bound, span)| {
|
||||
debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
|
||||
traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
|
||||
})
|
||||
.collect();
|
||||
debug!("check_type_bounds: item_bounds={:?}", obligations);
|
||||
|
||||
for mut obligation in util::elaborate(tcx, obligations) {
|
||||
let normalized_predicate = ocx.normalize(&normalize_cause, param_env, obligation.predicate);
|
||||
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
|
||||
obligation.predicate = normalized_predicate;
|
||||
|
||||
ocx.register_obligation(obligation);
|
||||
}
|
||||
// Check that all obligations are satisfied by the implementation's
|
||||
// version.
|
||||
let errors = ocx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||
return Err(reported);
|
||||
}
|
||||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
// lifetime parameters.
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
|
||||
}
|
||||
|
||||
/// Install projection predicates that allow GATs to project to their own
|
||||
/// definition types. This is not allowed in general in cases of default
|
||||
/// associated types in trait definitions, or when specialization is involved,
|
||||
/// but is needed when checking these definition types actually satisfy the
|
||||
/// trait bounds of the GAT.
|
||||
///
|
||||
/// # How it works
|
||||
///
|
||||
/// ```ignore (example)
|
||||
/// impl<A, B> Foo<u32> for (A, B) {
|
||||
/// type Bar<C> = Wrapper<A, B, C>
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - `impl_trait_ref` would be `<(A, B) as Foo<u32>>`
|
||||
/// - `normalize_impl_ty_args` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
|
||||
/// - `normalize_impl_ty` would be `Wrapper<A, B, ^0.0>`
|
||||
/// - `rebased_args` would be `[(A, B), u32, ^0.0]`, combining the args from
|
||||
/// the *trait* with the generic associated type parameters (as bound vars).
|
||||
///
|
||||
/// A note regarding the use of bound vars here:
|
||||
/// Imagine as an example
|
||||
/// ```
|
||||
/// trait Family {
|
||||
/// type Member<C: Eq>;
|
||||
/// }
|
||||
///
|
||||
/// impl Family for VecFamily {
|
||||
/// type Member<C: Eq> = i32;
|
||||
/// }
|
||||
/// ```
|
||||
/// Here, we would generate
|
||||
/// ```ignore (pseudo-rust)
|
||||
/// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
|
||||
/// ```
|
||||
///
|
||||
/// when we really would like to generate
|
||||
/// ```ignore (pseudo-rust)
|
||||
/// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
|
||||
/// ```
|
||||
///
|
||||
/// But, this is probably fine, because although the first clause can be used with types `C` that
|
||||
/// do not implement `Eq`, for it to cause some kind of problem, there would have to be a
|
||||
/// `VecFamily::Member<X>` for some type `X` where `!(X: Eq)`, that appears in the value of type
|
||||
/// `Member<C: Eq> = ....` That type would fail a well-formedness check that we ought to be doing
|
||||
/// elsewhere, which would check that any `<T as Family>::Member<X>` meets the bounds declared in
|
||||
/// the trait (notably, that `X: Eq` and `T: Family`).
|
||||
fn param_env_with_gat_bounds<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ty: ty::AssocItem,
|
||||
impl_ty: ty::AssocItem,
|
||||
impl_trait_ref: ty::TraitRef<'tcx>,
|
||||
) -> ty::ParamEnv<'tcx> {
|
||||
let param_env = tcx.param_env(impl_ty.def_id);
|
||||
let container_id = impl_ty.container_id(tcx);
|
||||
// Given
|
||||
//
|
||||
// impl<A, B> Foo<u32> for (A, B) {
|
||||
// type Bar<C> = Wrapper<A, B, C>
|
||||
// }
|
||||
//
|
||||
// - `impl_trait_ref` would be `<(A, B) as Foo<u32>>`
|
||||
// - `normalize_impl_ty_args` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
|
||||
// - `normalize_impl_ty` would be `Wrapper<A, B, ^0.0>`
|
||||
// - `rebased_args` would be `[(A, B), u32, ^0.0]`, combining the args from
|
||||
// the *trait* with the generic associated type parameters (as bound vars).
|
||||
//
|
||||
// A note regarding the use of bound vars here:
|
||||
// Imagine as an example
|
||||
// ```
|
||||
// trait Family {
|
||||
// type Member<C: Eq>;
|
||||
// }
|
||||
//
|
||||
// impl Family for VecFamily {
|
||||
// type Member<C: Eq> = i32;
|
||||
// }
|
||||
// ```
|
||||
// Here, we would generate
|
||||
// ```notrust
|
||||
// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
|
||||
// ```
|
||||
// when we really would like to generate
|
||||
// ```notrust
|
||||
// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
|
||||
// ```
|
||||
// But, this is probably fine, because although the first clause can be used with types C that
|
||||
// do not implement Eq, for it to cause some kind of problem, there would have to be a
|
||||
// VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type
|
||||
// Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing
|
||||
// elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in
|
||||
// the trait (notably, that X: Eq and T: Family).
|
||||
let mut predicates = param_env.caller_bounds().to_vec();
|
||||
|
||||
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
|
||||
smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).params.len());
|
||||
// Extend the impl's identity args with late-bound GAT vars
|
||||
@ -2257,105 +2352,30 @@ pub(super) fn check_type_bounds<'tcx>(
|
||||
let normalize_impl_ty = tcx.type_of(impl_ty.def_id).instantiate(tcx, normalize_impl_ty_args);
|
||||
let rebased_args = normalize_impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
|
||||
let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
|
||||
let normalize_param_env = {
|
||||
let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>();
|
||||
match normalize_impl_ty.kind() {
|
||||
ty::Alias(ty::Projection, proj)
|
||||
if proj.def_id == trait_ty.def_id && proj.args == rebased_args =>
|
||||
{
|
||||
// Don't include this predicate if the projected type is
|
||||
// exactly the same as the projection. This can occur in
|
||||
// (somewhat dubious) code like this:
|
||||
//
|
||||
// impl<T> X for T where T: X { type Y = <T as X>::Y; }
|
||||
}
|
||||
_ => predicates.push(
|
||||
ty::Binder::bind_with_vars(
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ty::AliasTy::new(tcx, trait_ty.def_id, rebased_args),
|
||||
term: normalize_impl_ty.into(),
|
||||
},
|
||||
bound_vars,
|
||||
)
|
||||
.to_predicate(tcx),
|
||||
),
|
||||
};
|
||||
ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
|
||||
};
|
||||
debug!(?normalize_param_env);
|
||||
|
||||
let impl_ty_def_id = impl_ty.def_id.expect_local();
|
||||
let impl_ty_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id);
|
||||
let rebased_args = impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
|
||||
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
|
||||
// A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the
|
||||
// span for an impl's associated type. Instead, for these, use the def_span for the synthesized
|
||||
// associated type.
|
||||
let impl_ty_span = if impl_ty.is_impl_trait_in_trait() {
|
||||
tcx.def_span(impl_ty_def_id)
|
||||
} else {
|
||||
match tcx.hir().get_by_def_id(impl_ty_def_id) {
|
||||
hir::Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Type(_, Some(ty)),
|
||||
..
|
||||
}) => ty.span,
|
||||
hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Type(ty), .. }) => ty.span,
|
||||
_ => bug!(),
|
||||
match normalize_impl_ty.kind() {
|
||||
ty::Alias(ty::Projection, proj)
|
||||
if proj.def_id == trait_ty.def_id && proj.args == rebased_args =>
|
||||
{
|
||||
// Don't include this predicate if the projected type is
|
||||
// exactly the same as the projection. This can occur in
|
||||
// (somewhat dubious) code like this:
|
||||
//
|
||||
// impl<T> X for T where T: X { type Y = <T as X>::Y; }
|
||||
}
|
||||
};
|
||||
let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl_ty_def_id)?;
|
||||
|
||||
let normalize_cause = ObligationCause::new(
|
||||
impl_ty_span,
|
||||
impl_ty_def_id,
|
||||
ObligationCauseCode::CheckAssociatedTypeBounds {
|
||||
impl_item_def_id: impl_ty.def_id.expect_local(),
|
||||
trait_item_def_id: trait_ty.def_id,
|
||||
},
|
||||
);
|
||||
let mk_cause = |span: Span| {
|
||||
let code = if span.is_dummy() {
|
||||
traits::ItemObligation(trait_ty.def_id)
|
||||
} else {
|
||||
traits::BindingObligation(trait_ty.def_id, span)
|
||||
};
|
||||
ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
|
||||
_ => predicates.push(
|
||||
ty::Binder::bind_with_vars(
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ty::AliasTy::new(tcx, trait_ty.def_id, rebased_args),
|
||||
term: normalize_impl_ty.into(),
|
||||
},
|
||||
bound_vars,
|
||||
)
|
||||
.to_predicate(tcx),
|
||||
),
|
||||
};
|
||||
|
||||
let obligations: Vec<_> = tcx
|
||||
.explicit_item_bounds(trait_ty.def_id)
|
||||
.iter_instantiated_copied(tcx, rebased_args)
|
||||
.map(|(concrete_ty_bound, span)| {
|
||||
debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
|
||||
traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
|
||||
})
|
||||
.collect();
|
||||
debug!("check_type_bounds: item_bounds={:?}", obligations);
|
||||
|
||||
for mut obligation in util::elaborate(tcx, obligations) {
|
||||
let normalized_predicate =
|
||||
ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate);
|
||||
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
|
||||
obligation.predicate = normalized_predicate;
|
||||
|
||||
ocx.register_obligation(obligation);
|
||||
}
|
||||
// Check that all obligations are satisfied by the implementation's
|
||||
// version.
|
||||
let errors = ocx.select_all_or_error();
|
||||
if !errors.is_empty() {
|
||||
let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||
return Err(reported);
|
||||
}
|
||||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
// lifetime parameters.
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
|
||||
ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
|
||||
}
|
||||
|
||||
fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
|
||||
|
@ -0,0 +1,18 @@
|
||||
// check-pass
|
||||
|
||||
#![feature(associated_type_defaults)]
|
||||
|
||||
trait Foo {
|
||||
type Bar<T>: Baz<Self> = i32;
|
||||
// We should be able to prove that `i32: Baz<Self>` because of
|
||||
// the impl below, which requires that `Self::Bar<()>: Eq<i32>`
|
||||
// which is true, because we assume `for<T> Self::Bar<T> = i32`.
|
||||
}
|
||||
|
||||
trait Baz<T: ?Sized> {}
|
||||
impl<T: Foo + ?Sized> Baz<T> for i32 where T::Bar<()>: Eq<i32> {}
|
||||
|
||||
trait Eq<T> {}
|
||||
impl<T> Eq<T> for T {}
|
||||
|
||||
fn main() {}
|
@ -10,7 +10,7 @@ trait Default {
|
||||
}
|
||||
|
||||
impl<T> Default for T {
|
||||
default type Id = T; //~ ERROR: type annotations needed
|
||||
default type Id = T;
|
||||
// This will be fixed by #111994
|
||||
fn intu(&self) -> &Self::Id { //~ ERROR type annotations needed
|
||||
self
|
||||
|
@ -16,13 +16,6 @@ LL | fn intu(&self) -> &Self::Id {
|
||||
|
|
||||
= note: cannot satisfy `<T as Default>::Id == _`
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/specialization-transmute.rs:13:23
|
||||
|
|
||||
LL | default type Id = T;
|
||||
| ^ cannot infer type for associated type `<T as Default>::Id`
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
error: aborting due to 2 previous errors; 1 warning emitted
|
||||
|
||||
Some errors have detailed explanations: E0282, E0284.
|
||||
For more information about an error, try `rustc --explain E0282`.
|
||||
For more information about this error, try `rustc --explain E0284`.
|
||||
|
@ -11,7 +11,7 @@ trait Default {
|
||||
}
|
||||
|
||||
impl<T> Default for T {
|
||||
default type Id = T; //~ ERROR type annotations needed
|
||||
default type Id = T;
|
||||
}
|
||||
|
||||
fn test<T: Default<Id = U>, U>() {}
|
||||
|
@ -20,13 +20,6 @@ note: required by a bound in `test`
|
||||
LL | fn test<T: Default<Id = U>, U>() {}
|
||||
| ^^^^^^ required by this bound in `test`
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/specialization-unconstrained.rs:14:22
|
||||
|
|
||||
LL | default type Id = T;
|
||||
| ^ cannot infer type for associated type `<T as Default>::Id`
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
error: aborting due to 2 previous errors; 1 warning emitted
|
||||
|
||||
Some errors have detailed explanations: E0282, E0284.
|
||||
For more information about an error, try `rustc --explain E0282`.
|
||||
For more information about this error, try `rustc --explain E0284`.
|
||||
|
Loading…
Reference in New Issue
Block a user