Skip to content

Commit

Permalink
Check associated type satisfy their bounds
Browse files Browse the repository at this point in the history
This was currently only happening due to eager normalization, which
isn't possible if there's specialization or bound variables.
  • Loading branch information
matthewjasper committed Jun 20, 2020
1 parent 04e589c commit d660dbc
Show file tree
Hide file tree
Showing 21 changed files with 625 additions and 6 deletions.
158 changes: 154 additions & 4 deletions src/librustc_typeck/check/compare_method.rs
Expand Up @@ -4,15 +4,17 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit;
use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_middle::ty;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::util::ExplicitSelf;
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt, WithConstness};
use rustc_span::Span;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal};

use super::{potentially_plural_count, FnCtxt, Inherited};
use std::iter;

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

compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)
compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)?;

compare_projection_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)
})();
}

/// The equivalent of [compare_predicate_entailment], but for associated types
/// instead of associated functions.
fn compare_type_predicate_entailment(
fn compare_type_predicate_entailment<'tcx>(
tcx: TyCtxt<'tcx>,
impl_ty: &ty::AssocItem,
impl_ty_span: Span,
Expand Down Expand Up @@ -1165,6 +1169,152 @@ fn compare_type_predicate_entailment(
})
}

/// Validate that `ProjectionCandidate`s created for this associated type will
/// be valid.
///
/// Usually given
///
/// trait X { type Y: Copy } impl X for T { type Y = S; }
///
/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
/// impl is well-formed we have to prove `S: Copy`.
///
/// 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.
fn compare_projection_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ty: &ty::AssocItem,
impl_ty: &ty::AssocItem,
impl_ty_span: Span,
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorReported> {
let is_gat = !tcx.generics_of(impl_ty.def_id).params.is_empty();
if impl_ty.defaultness.is_final() && !is_gat {
// For "final", non-generic associate type implementations, we
// don't need this as described above.
return Ok(());
}

let param_env = tcx.param_env(impl_ty.def_id);

let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.container.id());
let impl_ty_value = tcx.type_of(impl_ty.def_id);

// Map the predicate from the trait to the corresponding one for the impl.
// For example:
//
// trait X<A> { type Y<'a>: PartialEq<A> } impl X for T { type Y<'a> = &'a S; }
// impl<'x> X<&'x u32> for () { type Y<'c> = &'c u32; }
//
// For the `for<'a> <<Self as X<A>>::Y<'a>: PartialEq<A>` bound, this
// function would translate and partially normalize
// `[<Self as X<A>>::Y<'a>, A]` to `[&'a u32, &'x u32]`.
let translate_predicate_substs = move |predicate_substs: SubstsRef<'tcx>| {
let normalized_self = if !is_gat {
// projection_predicates only includes projections where the
// substs of the trait ref are exactly the trait's identity
// substs, so we can simply return the value from the impl.
impl_ty_value
} else {
let predicate_self_ty = predicate_substs.type_at(0);
let impl_ty_substs = if let ty::Projection(p) = predicate_self_ty.kind {
assert!(
p.item_def_id == trait_ty.def_id,
"projection_predicates returned predicate for the wrong type: {}",
predicate_self_ty,
);
p.substs.rebase_onto(tcx, impl_trait_ref.def_id, impl_substs)
} else {
bug!(
"projection_predicates returned predicate for the wrong type `{}`",
predicate_self_ty,
);
};
impl_ty_value.subst(tcx, impl_ty_substs)
};

tcx.mk_substs(
iter::once(normalized_self.into())
.chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, impl_trait_ref.substs))),
)
};

tcx.infer_ctxt().enter(move |infcx| {
let inh = Inherited::new(infcx, impl_ty.def_id.expect_local());
let infcx = &inh.infcx;
let mut selcx = traits::SelectionContext::new(&infcx);

let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id.expect_local());
let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
let cause = ObligationCause::new(
impl_ty_span,
impl_ty_hir_id,
ObligationCauseCode::ItemObligation(trait_ty.def_id),
);

let predicates = tcx.projection_predicates(trait_ty.def_id);

debug!("compare_projection_bounds: projection_predicates={:?}", predicates);

for predicate in predicates {
let concrete_ty_predicate = match predicate.kind() {
ty::PredicateKind::Trait(poly_tr, c) => poly_tr
.map_bound(|tr| {
let trait_substs = translate_predicate_substs(tr.trait_ref.substs);
ty::TraitRef { def_id: tr.def_id(), substs: trait_substs }
})
.with_constness(*c)
.to_predicate(tcx),
ty::PredicateKind::Projection(poly_projection) => poly_projection
.map_bound(|projection| {
let projection_substs =
translate_predicate_substs(projection.projection_ty.substs);
ty::ProjectionPredicate {
projection_ty: ty::ProjectionTy {
substs: projection_substs,
item_def_id: projection.projection_ty.item_def_id,
},
ty: projection.ty.subst(tcx, impl_trait_ref.substs),
}
})
.to_predicate(tcx),
_ => bug!("unexepected projection predicate kind: `{:?}`", predicate),
};

let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize(
&mut selcx,
param_env,
normalize_cause.clone(),
&concrete_ty_predicate,
);

debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);

inh.register_predicates(obligations);
inh.register_predicate(traits::Obligation::new(
cause.clone(),
param_env,
normalized_predicate,
));
}

// Check that all obligations are satisfied by the implementation's
// version.
if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) {
infcx.report_fulfillment_errors(errors, None, false);
return Err(ErrorReported);
}

// Finally, resolve all regions. This catches wily misuses of
// lifetime parameters.
let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id);
fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]);

Ok(())
})
}

fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
match impl_item.kind {
ty::AssocKind::Const => "const",
Expand Down
@@ -0,0 +1,32 @@
// Regression test for #68641

#![feature(generic_associated_types)]
//~^ WARNING the feature `generic_associated_types` is incomplete and may not

trait UnsafeCopy {
type Item<'a>: Copy;

fn copy<'a>(item: &Self::Item<'a>) -> Self::Item<'a> {
*item
}
}

impl<T> UnsafeCopy for T {
type Item<'a> = T;
//~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied
}

fn main() {
let mut s = String::from("Hello world!");

let copy = String::copy(&s);

// Do we indeed point to the samme memory?
assert!(s.as_ptr() == copy.as_ptr());

// Any use of `copy` is certeinly UB after this
drop(s);

// UB UB UB UB UB!!
println!("{}", copy);
}
@@ -0,0 +1,26 @@
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-68641-check-gat-bounds.rs:3:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information

error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
--> $DIR/issue-68641-check-gat-bounds.rs:15:5
|
LL | trait UnsafeCopy {
| ---------------- required by `UnsafeCopy`
...
LL | type Item<'a> = T;
| ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
|
help: consider restricting type parameter `T`
|
LL | impl<T: std::marker::Copy> UnsafeCopy for T {
| ^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
21 changes: 21 additions & 0 deletions src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs
@@ -0,0 +1,21 @@
// Regression test for #68642

#![feature(generic_associated_types)]
//~^ WARNING the feature `generic_associated_types` is incomplete and may not

trait Fun {
type F<'a>: Fn() -> u32;

fn callme<'a>(f: Self::F<'a>) -> u32 {
f()
}
}

impl<T> Fun for T {
type F<'a> = Self;
//~^ ERROR expected a `std::ops::Fn<()>` closure, found `T`
}

fn main() {
<fn() -> usize>::callme(|| 1);
}
@@ -0,0 +1,28 @@
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-68642-broken-llvm-ir.rs:3:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information

error[E0277]: expected a `std::ops::Fn<()>` closure, found `T`
--> $DIR/issue-68642-broken-llvm-ir.rs:15:5
|
LL | trait Fun {
| --------- required by `Fun`
...
LL | type F<'a> = Self;
| ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T`
|
= help: the trait `std::ops::Fn<()>` is not implemented for `T`
= note: wrap the `T` in a closure with no arguments: `|| { /* code */ }
help: consider restricting type parameter `T`
|
LL | impl<T: std::ops::Fn<()>> Fun for T {
| ^^^^^^^^^^^^^^^^^^

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
21 changes: 21 additions & 0 deletions src/test/ui/generic-associated-types/issue-68643-broken-mir.rs
@@ -0,0 +1,21 @@
// Regression test for #68643

#![feature(generic_associated_types)]
//~^ WARNING the feature `generic_associated_types` is incomplete and may not

trait Fun {
type F<'a>: Fn() -> u32;

fn callme<'a>(f: Self::F<'a>) -> u32 {
f()
}
}

impl<T> Fun for T {
type F<'a> = Self;
//~^ ERROR expected a `std::ops::Fn<()>` closure, found `T`
}

pub fn main() {
<fn()>::callme(|| {});
}
28 changes: 28 additions & 0 deletions src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr
@@ -0,0 +1,28 @@
warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/issue-68643-broken-mir.rs:3:12
|
LL | #![feature(generic_associated_types)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44265 <https://github.com/rust-lang/rust/issues/44265> for more information

error[E0277]: expected a `std::ops::Fn<()>` closure, found `T`
--> $DIR/issue-68643-broken-mir.rs:15:5
|
LL | trait Fun {
| --------- required by `Fun`
...
LL | type F<'a> = Self;
| ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T`
|
= help: the trait `std::ops::Fn<()>` is not implemented for `T`
= note: wrap the `T` in a closure with no arguments: `|| { /* code */ }
help: consider restricting type parameter `T`
|
LL | impl<T: std::ops::Fn<()>> Fun for T {
| ^^^^^^^^^^^^^^^^^^

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
@@ -0,0 +1,21 @@
// Regression test for #68644

#![feature(generic_associated_types)]
//~^ WARNING the feature `generic_associated_types` is incomplete and may not

trait Fun {
type F<'a>: Fn() -> u32;

fn callme<'a>(f: Self::F<'a>) -> u32 {
f()
}
}

impl<T> Fun for T {
type F<'a> = Self;
//~^ ERROR expected a `std::ops::Fn<()>` closure, found `T`
}

fn main() {
<u8>::callme(0);
}

0 comments on commit d660dbc

Please sign in to comment.