Skip to content

Commit fb16ee7

Browse files
committed
Check associated type satisfy their bounds
This was currently only happening due to eager normalization, which isn't possible if there's specialization or bound variables.
1 parent d14ee0b commit fb16ee7

21 files changed

+597
-5
lines changed

src/librustc_typeck/check/compare_method.rs

+154-4
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ use rustc_hir::def::{DefKind, Res};
44
use rustc_hir::intravisit;
55
use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
66
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
7+
use rustc_middle::ty;
78
use rustc_middle::ty::error::{ExpectedFound, TypeError};
8-
use rustc_middle::ty::subst::{InternalSubsts, Subst};
9+
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
910
use rustc_middle::ty::util::ExplicitSelf;
10-
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
11+
use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt, WithConstness};
1112
use rustc_span::Span;
1213
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
1314
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
1415

1516
use super::{potentially_plural_count, FnCtxt, Inherited};
17+
use std::iter;
1618

1719
/// Checks that a method from an impl conforms to the signature of
1820
/// the same method as declared in the trait.
@@ -1053,13 +1055,15 @@ crate fn compare_ty_impl<'tcx>(
10531055
let _: Result<(), ErrorReported> = (|| {
10541056
compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?;
10551057

1056-
compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)
1058+
compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)?;
1059+
1060+
compare_projection_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)
10571061
})();
10581062
}
10591063

10601064
/// The equivalent of [compare_predicate_entailment], but for associated types
10611065
/// instead of associated functions.
1062-
fn compare_type_predicate_entailment(
1066+
fn compare_type_predicate_entailment<'tcx>(
10631067
tcx: TyCtxt<'tcx>,
10641068
impl_ty: &ty::AssocItem,
10651069
impl_ty_span: Span,
@@ -1161,6 +1165,152 @@ fn compare_type_predicate_entailment(
11611165
})
11621166
}
11631167

1168+
/// Validate that `ProjectionCandidate`s created for this associated type will
1169+
/// be valid.
1170+
///
1171+
/// Usually given
1172+
///
1173+
/// trait X { type Y: Copy } impl X for T { type Y = S; }
1174+
///
1175+
/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
1176+
/// impl is well-formed we have to prove `S: Copy`.
1177+
///
1178+
/// For default associated types the normalization is not possible (the value
1179+
/// from the impl could be overridden). We also can't normalize generic
1180+
/// associated types (yet) because they contain bound parameters.
1181+
fn compare_projection_bounds<'tcx>(
1182+
tcx: TyCtxt<'tcx>,
1183+
trait_ty: &ty::AssocItem,
1184+
impl_ty: &ty::AssocItem,
1185+
impl_ty_span: Span,
1186+
impl_trait_ref: ty::TraitRef<'tcx>,
1187+
) -> Result<(), ErrorReported> {
1188+
let is_gat = !tcx.generics_of(impl_ty.def_id).params.is_empty();
1189+
if impl_ty.defaultness.is_final() && !is_gat {
1190+
// For "final", non-generic associate type implementations, we
1191+
// don't need this as described above.
1192+
return Ok(());
1193+
}
1194+
1195+
let param_env = tcx.param_env(impl_ty.def_id);
1196+
1197+
let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.container.id());
1198+
let impl_ty_value = tcx.type_of(impl_ty.def_id);
1199+
1200+
// Map the predicate from the trait to the corresponding one for the impl.
1201+
// For example:
1202+
//
1203+
// trait X<A> { type Y<'a>: PartialEq<A> } impl X for T { type Y<'a> = &'a S; }
1204+
// impl<'x> X<&'x u32> for () { type Y<'c> = &'c u32; }
1205+
//
1206+
// For the `for<'a> <<Self as X<A>>::Y<'a>: PartialEq<A>` bound, this
1207+
// function would translate and partially normalize
1208+
// `[<Self as X<A>>::Y<'a>, A]` to `[&'a u32, &'x u32]`.
1209+
let translate_predicate_substs = move |predicate_substs: SubstsRef<'tcx>| {
1210+
let normalized_self = if !is_gat {
1211+
// projection_predicates only includes projections where the
1212+
// substs of the trait ref are exactly the trait's identity
1213+
// substs, so we can simply return the value from the impl.
1214+
impl_ty_value
1215+
} else {
1216+
let predicate_self_ty = predicate_substs.type_at(0);
1217+
let impl_ty_substs = if let ty::Projection(p) = predicate_self_ty.kind {
1218+
assert!(
1219+
p.item_def_id == trait_ty.def_id,
1220+
"projection_predicates returned predicate for the wrong type: {}",
1221+
predicate_self_ty,
1222+
);
1223+
p.substs.rebase_onto(tcx, impl_trait_ref.def_id, impl_substs)
1224+
} else {
1225+
bug!(
1226+
"projection_predicates returned predicate for the wrong type `{}`",
1227+
predicate_self_ty,
1228+
);
1229+
};
1230+
impl_ty_value.subst(tcx, impl_ty_substs)
1231+
};
1232+
1233+
tcx.mk_substs(
1234+
iter::once(normalized_self.into())
1235+
.chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, impl_trait_ref.substs))),
1236+
)
1237+
};
1238+
1239+
tcx.infer_ctxt().enter(move |infcx| {
1240+
let inh = Inherited::new(infcx, impl_ty.def_id.expect_local());
1241+
let infcx = &inh.infcx;
1242+
let mut selcx = traits::SelectionContext::new(&infcx);
1243+
1244+
let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id.expect_local());
1245+
let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
1246+
let cause = ObligationCause {
1247+
span: impl_ty_span,
1248+
body_id: impl_ty_hir_id,
1249+
code: ObligationCauseCode::ItemObligation(impl_trait_ref.def_id),
1250+
};
1251+
1252+
let predicates = tcx.projection_predicates(trait_ty.def_id);
1253+
1254+
debug!("compare_projection_bounds: projection_predicates={:?}", predicates);
1255+
1256+
for predicate in predicates {
1257+
let concrete_ty_predicate = match predicate.kind() {
1258+
ty::PredicateKind::Trait(poly_tr, c) => poly_tr
1259+
.map_bound(|tr| {
1260+
let trait_substs = translate_predicate_substs(tr.trait_ref.substs);
1261+
ty::TraitRef { def_id: tr.def_id(), substs: trait_substs }
1262+
})
1263+
.with_constness(*c)
1264+
.to_predicate(tcx),
1265+
ty::PredicateKind::Projection(poly_projection) => poly_projection
1266+
.map_bound(|projection| {
1267+
let projection_substs =
1268+
translate_predicate_substs(projection.projection_ty.substs);
1269+
ty::ProjectionPredicate {
1270+
projection_ty: ty::ProjectionTy {
1271+
substs: projection_substs,
1272+
item_def_id: projection.projection_ty.item_def_id,
1273+
},
1274+
ty: projection.ty.subst(tcx, impl_trait_ref.substs),
1275+
}
1276+
})
1277+
.to_predicate(tcx),
1278+
_ => bug!("unexepected projection predicate kind: `{:?}`", predicate),
1279+
};
1280+
1281+
let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize(
1282+
&mut selcx,
1283+
param_env,
1284+
normalize_cause.clone(),
1285+
&concrete_ty_predicate,
1286+
);
1287+
1288+
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
1289+
1290+
inh.register_predicates(obligations);
1291+
inh.register_predicate(traits::Obligation::new(
1292+
cause.clone(),
1293+
param_env,
1294+
normalized_predicate,
1295+
));
1296+
}
1297+
1298+
// Check that all obligations are satisfied by the implementation's
1299+
// version.
1300+
if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) {
1301+
infcx.report_fulfillment_errors(errors, None, false);
1302+
return Err(ErrorReported);
1303+
}
1304+
1305+
// Finally, resolve all regions. This catches wily misuses of
1306+
// lifetime parameters.
1307+
let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
1308+
fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]);
1309+
1310+
Ok(())
1311+
})
1312+
}
1313+
11641314
fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
11651315
match impl_item.kind {
11661316
ty::AssocKind::Const => "const",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Regression test for #68641
2+
3+
#![feature(generic_associated_types)]
4+
//~^ WARNING the feature `generic_associated_types` is incomplete and may not
5+
6+
trait UnsafeCopy {
7+
type Item<'a>: Copy;
8+
9+
fn copy<'a>(item: &Self::Item<'a>) -> Self::Item<'a> {
10+
*item
11+
}
12+
}
13+
14+
impl<T> UnsafeCopy for T {
15+
type Item<'a> = T;
16+
//~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied
17+
}
18+
19+
fn main() {
20+
let mut s = String::from("Hello world!");
21+
22+
let copy = String::copy(&s);
23+
24+
// Do we indeed point to the samme memory?
25+
assert!(s.as_ptr() == copy.as_ptr());
26+
27+
// Any use of `copy` is certeinly UB after this
28+
drop(s);
29+
30+
// UB UB UB UB UB!!
31+
println!("{}", copy);
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/issue-68641-check-gat-bounds.rs:3:12
3+
|
4+
LL | #![feature(generic_associated_types)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
9+
10+
error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
11+
--> $DIR/issue-68641-check-gat-bounds.rs:15:5
12+
|
13+
LL | trait UnsafeCopy {
14+
| ---------------- required by `UnsafeCopy`
15+
...
16+
LL | type Item<'a> = T;
17+
| ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
18+
|
19+
help: consider restricting type parameter `T`
20+
|
21+
LL | impl<T: std::marker::Copy> UnsafeCopy for T {
22+
| ^^^^^^^^^^^^^^^^^^^
23+
24+
error: aborting due to previous error; 1 warning emitted
25+
26+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression test for #68642
2+
3+
#![feature(generic_associated_types)]
4+
//~^ WARNING the feature `generic_associated_types` is incomplete and may not
5+
6+
trait Fun {
7+
type F<'a>: Fn() -> u32;
8+
9+
fn callme<'a>(f: Self::F<'a>) -> u32 {
10+
f()
11+
}
12+
}
13+
14+
impl<T> Fun for T {
15+
type F<'a> = Self;
16+
//~^ ERROR expected a `std::ops::Fn<()>` closure, found `T`
17+
}
18+
19+
fn main() {
20+
<fn() -> usize>::callme(|| 1);
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/issue-68642-broken-llvm-ir.rs:3:12
3+
|
4+
LL | #![feature(generic_associated_types)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
9+
10+
error[E0277]: expected a `std::ops::Fn<()>` closure, found `T`
11+
--> $DIR/issue-68642-broken-llvm-ir.rs:15:5
12+
|
13+
LL | trait Fun {
14+
| --------- required by `Fun`
15+
...
16+
LL | type F<'a> = Self;
17+
| ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T`
18+
|
19+
= help: the trait `std::ops::Fn<()>` is not implemented for `T`
20+
= note: wrap the `T` in a closure with no arguments: `|| { /* code */ }
21+
help: consider restricting type parameter `T`
22+
|
23+
LL | impl<T: std::ops::Fn<()>> Fun for T {
24+
| ^^^^^^^^^^^^^^^^^^
25+
26+
error: aborting due to previous error; 1 warning emitted
27+
28+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression test for #68643
2+
3+
#![feature(generic_associated_types)]
4+
//~^ WARNING the feature `generic_associated_types` is incomplete and may not
5+
6+
trait Fun {
7+
type F<'a>: Fn() -> u32;
8+
9+
fn callme<'a>(f: Self::F<'a>) -> u32 {
10+
f()
11+
}
12+
}
13+
14+
impl<T> Fun for T {
15+
type F<'a> = Self;
16+
//~^ ERROR expected a `std::ops::Fn<()>` closure, found `T`
17+
}
18+
19+
pub fn main() {
20+
<fn()>::callme(|| {});
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/issue-68643-broken-mir.rs:3:12
3+
|
4+
LL | #![feature(generic_associated_types)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information
9+
10+
error[E0277]: expected a `std::ops::Fn<()>` closure, found `T`
11+
--> $DIR/issue-68643-broken-mir.rs:15:5
12+
|
13+
LL | trait Fun {
14+
| --------- required by `Fun`
15+
...
16+
LL | type F<'a> = Self;
17+
| ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T`
18+
|
19+
= help: the trait `std::ops::Fn<()>` is not implemented for `T`
20+
= note: wrap the `T` in a closure with no arguments: `|| { /* code */ }
21+
help: consider restricting type parameter `T`
22+
|
23+
LL | impl<T: std::ops::Fn<()>> Fun for T {
24+
| ^^^^^^^^^^^^^^^^^^
25+
26+
error: aborting due to previous error; 1 warning emitted
27+
28+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression test for #68644
2+
3+
#![feature(generic_associated_types)]
4+
//~^ WARNING the feature `generic_associated_types` is incomplete and may not
5+
6+
trait Fun {
7+
type F<'a>: Fn() -> u32;
8+
9+
fn callme<'a>(f: Self::F<'a>) -> u32 {
10+
f()
11+
}
12+
}
13+
14+
impl<T> Fun for T {
15+
type F<'a> = Self;
16+
//~^ ERROR expected a `std::ops::Fn<()>` closure, found `T`
17+
}
18+
19+
fn main() {
20+
<u8>::callme(0);
21+
}

0 commit comments

Comments
 (0)