Skip to content

Commit d660dbc

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 04e589c commit d660dbc

21 files changed

+625
-6
lines changed

src/librustc_typeck/check/compare_method.rs

Lines changed: 154 additions & 4 deletions
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.
@@ -1057,13 +1059,15 @@ crate fn compare_ty_impl<'tcx>(
10571059
let _: Result<(), ErrorReported> = (|| {
10581060
compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?;
10591061

1060-
compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)
1062+
compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)?;
1063+
1064+
compare_projection_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)
10611065
})();
10621066
}
10631067

10641068
/// The equivalent of [compare_predicate_entailment], but for associated types
10651069
/// instead of associated functions.
1066-
fn compare_type_predicate_entailment(
1070+
fn compare_type_predicate_entailment<'tcx>(
10671071
tcx: TyCtxt<'tcx>,
10681072
impl_ty: &ty::AssocItem,
10691073
impl_ty_span: Span,
@@ -1165,6 +1169,152 @@ fn compare_type_predicate_entailment(
11651169
})
11661170
}
11671171

1172+
/// Validate that `ProjectionCandidate`s created for this associated type will
1173+
/// be valid.
1174+
///
1175+
/// Usually given
1176+
///
1177+
/// trait X { type Y: Copy } impl X for T { type Y = S; }
1178+
///
1179+
/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
1180+
/// impl is well-formed we have to prove `S: Copy`.
1181+
///
1182+
/// For default associated types the normalization is not possible (the value
1183+
/// from the impl could be overridden). We also can't normalize generic
1184+
/// associated types (yet) because they contain bound parameters.
1185+
fn compare_projection_bounds<'tcx>(
1186+
tcx: TyCtxt<'tcx>,
1187+
trait_ty: &ty::AssocItem,
1188+
impl_ty: &ty::AssocItem,
1189+
impl_ty_span: Span,
1190+
impl_trait_ref: ty::TraitRef<'tcx>,
1191+
) -> Result<(), ErrorReported> {
1192+
let is_gat = !tcx.generics_of(impl_ty.def_id).params.is_empty();
1193+
if impl_ty.defaultness.is_final() && !is_gat {
1194+
// For "final", non-generic associate type implementations, we
1195+
// don't need this as described above.
1196+
return Ok(());
1197+
}
1198+
1199+
let param_env = tcx.param_env(impl_ty.def_id);
1200+
1201+
let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.container.id());
1202+
let impl_ty_value = tcx.type_of(impl_ty.def_id);
1203+
1204+
// Map the predicate from the trait to the corresponding one for the impl.
1205+
// For example:
1206+
//
1207+
// trait X<A> { type Y<'a>: PartialEq<A> } impl X for T { type Y<'a> = &'a S; }
1208+
// impl<'x> X<&'x u32> for () { type Y<'c> = &'c u32; }
1209+
//
1210+
// For the `for<'a> <<Self as X<A>>::Y<'a>: PartialEq<A>` bound, this
1211+
// function would translate and partially normalize
1212+
// `[<Self as X<A>>::Y<'a>, A]` to `[&'a u32, &'x u32]`.
1213+
let translate_predicate_substs = move |predicate_substs: SubstsRef<'tcx>| {
1214+
let normalized_self = if !is_gat {
1215+
// projection_predicates only includes projections where the
1216+
// substs of the trait ref are exactly the trait's identity
1217+
// substs, so we can simply return the value from the impl.
1218+
impl_ty_value
1219+
} else {
1220+
let predicate_self_ty = predicate_substs.type_at(0);
1221+
let impl_ty_substs = if let ty::Projection(p) = predicate_self_ty.kind {
1222+
assert!(
1223+
p.item_def_id == trait_ty.def_id,
1224+
"projection_predicates returned predicate for the wrong type: {}",
1225+
predicate_self_ty,
1226+
);
1227+
p.substs.rebase_onto(tcx, impl_trait_ref.def_id, impl_substs)
1228+
} else {
1229+
bug!(
1230+
"projection_predicates returned predicate for the wrong type `{}`",
1231+
predicate_self_ty,
1232+
);
1233+
};
1234+
impl_ty_value.subst(tcx, impl_ty_substs)
1235+
};
1236+
1237+
tcx.mk_substs(
1238+
iter::once(normalized_self.into())
1239+
.chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, impl_trait_ref.substs))),
1240+
)
1241+
};
1242+
1243+
tcx.infer_ctxt().enter(move |infcx| {
1244+
let inh = Inherited::new(infcx, impl_ty.def_id.expect_local());
1245+
let infcx = &inh.infcx;
1246+
let mut selcx = traits::SelectionContext::new(&infcx);
1247+
1248+
let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id.expect_local());
1249+
let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
1250+
let cause = ObligationCause::new(
1251+
impl_ty_span,
1252+
impl_ty_hir_id,
1253+
ObligationCauseCode::ItemObligation(trait_ty.def_id),
1254+
);
1255+
1256+
let predicates = tcx.projection_predicates(trait_ty.def_id);
1257+
1258+
debug!("compare_projection_bounds: projection_predicates={:?}", predicates);
1259+
1260+
for predicate in predicates {
1261+
let concrete_ty_predicate = match predicate.kind() {
1262+
ty::PredicateKind::Trait(poly_tr, c) => poly_tr
1263+
.map_bound(|tr| {
1264+
let trait_substs = translate_predicate_substs(tr.trait_ref.substs);
1265+
ty::TraitRef { def_id: tr.def_id(), substs: trait_substs }
1266+
})
1267+
.with_constness(*c)
1268+
.to_predicate(tcx),
1269+
ty::PredicateKind::Projection(poly_projection) => poly_projection
1270+
.map_bound(|projection| {
1271+
let projection_substs =
1272+
translate_predicate_substs(projection.projection_ty.substs);
1273+
ty::ProjectionPredicate {
1274+
projection_ty: ty::ProjectionTy {
1275+
substs: projection_substs,
1276+
item_def_id: projection.projection_ty.item_def_id,
1277+
},
1278+
ty: projection.ty.subst(tcx, impl_trait_ref.substs),
1279+
}
1280+
})
1281+
.to_predicate(tcx),
1282+
_ => bug!("unexepected projection predicate kind: `{:?}`", predicate),
1283+
};
1284+
1285+
let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize(
1286+
&mut selcx,
1287+
param_env,
1288+
normalize_cause.clone(),
1289+
&concrete_ty_predicate,
1290+
);
1291+
1292+
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
1293+
1294+
inh.register_predicates(obligations);
1295+
inh.register_predicate(traits::Obligation::new(
1296+
cause.clone(),
1297+
param_env,
1298+
normalized_predicate,
1299+
));
1300+
}
1301+
1302+
// Check that all obligations are satisfied by the implementation's
1303+
// version.
1304+
if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) {
1305+
infcx.report_fulfillment_errors(errors, None, false);
1306+
return Err(ErrorReported);
1307+
}
1308+
1309+
// Finally, resolve all regions. This catches wily misuses of
1310+
// lifetime parameters.
1311+
let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
1312+
fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]);
1313+
1314+
Ok(())
1315+
})
1316+
}
1317+
11681318
fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
11691319
match impl_item.kind {
11701320
ty::AssocKind::Const => "const",
Lines changed: 32 additions & 0 deletions
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+
}
Lines changed: 26 additions & 0 deletions
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`.
Lines changed: 21 additions & 0 deletions
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+
}
Lines changed: 28 additions & 0 deletions
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`.
Lines changed: 21 additions & 0 deletions
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+
}
Lines changed: 28 additions & 0 deletions
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`.
Lines changed: 21 additions & 0 deletions
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)