mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-31 00:53:48 +00:00
Auto merge of #87900 - jackh726:issue-87429, r=nikomatsakis
Use bound vars for GAT params in param_env in check_type_bounds Fixes #87429
This commit is contained in:
commit
b5fe3bc065
@ -234,7 +234,7 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
fn fill_item<F>(
|
||||
pub fn fill_item<F>(
|
||||
substs: &mut SmallVec<[GenericArg<'tcx>; 8]>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
defs: &ty::Generics,
|
||||
@ -249,7 +249,7 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
|
||||
Self::fill_single(substs, defs, mk_kind)
|
||||
}
|
||||
|
||||
fn fill_single<F>(
|
||||
pub fn fill_single<F>(
|
||||
substs: &mut SmallVec<[GenericArg<'tcx>; 8]>,
|
||||
defs: &ty::Generics,
|
||||
mk_kind: &mut F,
|
||||
|
@ -1225,6 +1225,7 @@ fn compare_type_predicate_entailment<'tcx>(
|
||||
/// For default associated types the normalization is not possible (the value
|
||||
/// from the impl could be overridden). We also can't normalize generic
|
||||
/// associated types (yet) because they contain bound parameters.
|
||||
#[tracing::instrument(level = "debug", skip(tcx))]
|
||||
pub fn check_type_bounds<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ty: &ty::AssocItem,
|
||||
@ -1238,10 +1239,83 @@ pub fn check_type_bounds<'tcx>(
|
||||
// type Bar<C> =...
|
||||
// }
|
||||
//
|
||||
// - `impl_substs` would be `[A, B, C]`
|
||||
// - `rebased_substs` would be `[(A, B), u32, C]`, combining the substs from
|
||||
// the *trait* with the generic associated type parameters.
|
||||
let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
|
||||
// - `impl_trait_ref` would be `<(A, B) as Foo<u32>>
|
||||
// - `impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
|
||||
// - `rebased_substs` would be `[(A, B), u32, ^0.0]`, combining the substs 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 defs: &ty::Generics = tcx.generics_of(impl_ty.def_id);
|
||||
let mut substs = smallvec::SmallVec::with_capacity(defs.count());
|
||||
if let Some(def_id) = defs.parent {
|
||||
let parent_defs = tcx.generics_of(def_id);
|
||||
InternalSubsts::fill_item(&mut substs, tcx, parent_defs, &mut |param, _| {
|
||||
tcx.mk_param_from_def(param)
|
||||
});
|
||||
}
|
||||
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
|
||||
smallvec::SmallVec::with_capacity(defs.count());
|
||||
InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param.kind {
|
||||
GenericParamDefKind::Type { .. } => {
|
||||
let kind = ty::BoundTyKind::Param(param.name);
|
||||
let bound_var = ty::BoundVariableKind::Ty(kind);
|
||||
bound_vars.push(bound_var);
|
||||
tcx.mk_ty(ty::Bound(
|
||||
ty::INNERMOST,
|
||||
ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
|
||||
))
|
||||
.into()
|
||||
}
|
||||
GenericParamDefKind::Lifetime => {
|
||||
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
|
||||
let bound_var = ty::BoundVariableKind::Region(kind);
|
||||
bound_vars.push(bound_var);
|
||||
tcx.mk_region(ty::ReLateBound(
|
||||
ty::INNERMOST,
|
||||
ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
|
||||
))
|
||||
.into()
|
||||
}
|
||||
GenericParamDefKind::Const { .. } => {
|
||||
let bound_var = ty::BoundVariableKind::Const;
|
||||
bound_vars.push(bound_var);
|
||||
tcx.mk_const(ty::Const {
|
||||
ty: tcx.type_of(param.def_id),
|
||||
val: ty::ConstKind::Bound(
|
||||
ty::INNERMOST,
|
||||
ty::BoundVar::from_usize(bound_vars.len() - 1),
|
||||
),
|
||||
})
|
||||
.into()
|
||||
}
|
||||
});
|
||||
let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter());
|
||||
let impl_ty_substs = tcx.intern_substs(&substs);
|
||||
|
||||
let rebased_substs =
|
||||
impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs);
|
||||
let impl_ty_value = tcx.type_of(impl_ty.def_id);
|
||||
@ -1270,18 +1344,26 @@ pub fn check_type_bounds<'tcx>(
|
||||
// impl<T> X for T where T: X { type Y = <T as X>::Y; }
|
||||
}
|
||||
_ => predicates.push(
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
projection_ty: ty::ProjectionTy {
|
||||
item_def_id: trait_ty.def_id,
|
||||
substs: rebased_substs,
|
||||
ty::Binder::bind_with_vars(
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ty::ProjectionTy {
|
||||
item_def_id: trait_ty.def_id,
|
||||
substs: rebased_substs,
|
||||
},
|
||||
ty: impl_ty_value,
|
||||
},
|
||||
ty: impl_ty_value,
|
||||
})
|
||||
bound_vars,
|
||||
)
|
||||
.to_predicate(tcx),
|
||||
),
|
||||
};
|
||||
ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing)
|
||||
};
|
||||
debug!(?normalize_param_env);
|
||||
|
||||
let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
|
||||
let rebased_substs =
|
||||
impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs);
|
||||
|
||||
tcx.infer_ctxt().enter(move |infcx| {
|
||||
let constness = impl_ty
|
||||
@ -1308,6 +1390,7 @@ pub fn check_type_bounds<'tcx>(
|
||||
.explicit_item_bounds(trait_ty.def_id)
|
||||
.iter()
|
||||
.map(|&(bound, span)| {
|
||||
debug!(?bound);
|
||||
let concrete_ty_bound = bound.subst(tcx, rebased_substs);
|
||||
debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
|
||||
|
||||
|
20
src/test/ui/generic-associated-types/issue-87429-2.rs
Normal file
20
src/test/ui/generic-associated-types/issue-87429-2.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Derived from `issue-87429`. A test that ensures that using bound vars in the
|
||||
// predicates in the param env when checking that an associated type satisfies
|
||||
// its bounds does not cause us to not be able to use the bounds on the parameters.
|
||||
|
||||
// check-pass
|
||||
|
||||
#![feature(generic_associated_types)]
|
||||
|
||||
trait Family {
|
||||
type Member<'a, C: Eq>: for<'b> MyBound<'b, C>;
|
||||
}
|
||||
|
||||
trait MyBound<'a, C> { }
|
||||
impl<'a, C: Eq> MyBound<'a, C> for i32 { }
|
||||
|
||||
impl Family for () {
|
||||
type Member<'a, C: Eq> = i32;
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,18 @@
|
||||
// check-fail
|
||||
|
||||
#![feature(associated_type_defaults)]
|
||||
#![feature(generic_associated_types)]
|
||||
|
||||
trait Family {
|
||||
// Fine, i32: PartialEq<i32>
|
||||
type Member<'a>: for<'b> PartialEq<Self::Member<'b>> = i32;
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
trait Family2 {
|
||||
// Not fine, not Foo: PartialEq<Foo>
|
||||
type Member<'a>: for<'b> PartialEq<Self::Member<'b>> = Foo;
|
||||
//~^ ERROR can't compare
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,16 @@
|
||||
error[E0277]: can't compare `Foo` with `Foo`
|
||||
--> $DIR/issue-87429-associated-type-default.rs:14:5
|
||||
|
|
||||
LL | type Member<'a>: for<'b> PartialEq<Self::Member<'b>> = Foo;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `Foo == Foo`
|
||||
|
|
||||
= help: the trait `PartialEq` is not implemented for `Foo`
|
||||
note: required by a bound in `Family2::Member`
|
||||
--> $DIR/issue-87429-associated-type-default.rs:14:22
|
||||
|
|
||||
LL | type Member<'a>: for<'b> PartialEq<Self::Member<'b>> = Foo;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Family2::Member`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -0,0 +1,25 @@
|
||||
// check-fail
|
||||
|
||||
#![feature(specialization)]
|
||||
//~^ WARN incomplete
|
||||
#![feature(generic_associated_types)]
|
||||
|
||||
trait Family {
|
||||
type Member<'a>: for<'b> PartialEq<Self::Member<'b>>;
|
||||
}
|
||||
|
||||
struct I32Family;
|
||||
|
||||
impl Family for I32Family {
|
||||
default type Member<'a> = i32;
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
struct FooFamily;
|
||||
|
||||
impl Family for FooFamily {
|
||||
default type Member<'a> = Foo;
|
||||
//~^ ERROR can't compare
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,26 @@
|
||||
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/issue-87429-specialization.rs:3:12
|
||||
|
|
||||
LL | #![feature(specialization)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
|
||||
= help: consider using `min_specialization` instead, which is more stable and complete
|
||||
|
||||
error[E0277]: can't compare `Foo` with `Foo`
|
||||
--> $DIR/issue-87429-specialization.rs:21:5
|
||||
|
|
||||
LL | default type Member<'a> = Foo;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `Foo == Foo`
|
||||
|
|
||||
= help: the trait `PartialEq` is not implemented for `Foo`
|
||||
note: required by a bound in `Family::Member`
|
||||
--> $DIR/issue-87429-specialization.rs:8:22
|
||||
|
|
||||
LL | type Member<'a>: for<'b> PartialEq<Self::Member<'b>>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Family::Member`
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
15
src/test/ui/generic-associated-types/issue-87429.rs
Normal file
15
src/test/ui/generic-associated-types/issue-87429.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// check-pass
|
||||
|
||||
#![feature(generic_associated_types)]
|
||||
|
||||
trait Family {
|
||||
type Member<'a>: for<'b> PartialEq<Self::Member<'b>>;
|
||||
}
|
||||
|
||||
struct I32;
|
||||
|
||||
impl Family for I32 {
|
||||
type Member<'a> = i32;
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user