Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

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)