diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 4ffc3bf8e78f..959feb766ee0 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -425,6 +425,78 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { for_item(tcx, item).with_fcx(|fcx, _| { check_where_clauses(tcx, fcx, item.span, trait_def_id, None); + + // Type-check associated type defaults (if there are any): + // Assuming the defaults are used, check that all predicates (bounds on + // the assoc type and where clauses on the trait) hold. + + let substs = InternalSubsts::identity_for_item(tcx, trait_def_id); + + // For all assoc. types with defaults, build a map from + // `>::Assoc` to the default type. + let map = tcx.associated_items(trait_def_id) + .filter_map(|item| { + if item.kind == ty::AssocKind::Type && item.defaultness.has_value() { + // `>::Assoc` + let proj = ty::ProjectionTy { + substs, + item_def_id: item.def_id, + }; + let default_ty = tcx.type_of(item.def_id); + debug!("assoc. type default mapping: {} -> {}", proj, default_ty); + Some((proj, default_ty)) + } else { + None + } + }) + .collect::>(); + + struct DefaultNormalizer<'tcx> { + tcx: TyCtxt<'tcx>, + map: FxHashMap, Ty<'tcx>>, + } + + impl<'tcx> ty::fold::TypeFolder<'tcx> for DefaultNormalizer<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.sty { + ty::Projection(proj_ty) => { + if let Some(default) = self.map.get(&proj_ty) { + default + } else { + t.super_fold_with(self) + } + } + _ => t.super_fold_with(self), + } + } + } + + // Now take all predicates defined on the trait, replace any mention of + // the assoc. types with their default, and prove them. + // We only consider predicates that directly mention the assoc. type. + let mut norm = DefaultNormalizer { tcx, map }; + let predicates = fcx.tcx.predicates_of(trait_def_id); + for &(orig_pred, span) in predicates.predicates.iter() { + let pred = orig_pred.fold_with(&mut norm); + if pred != orig_pred { + // Mentions one of the defaulted assoc. types + debug!("default suitability check: proving predicate: {} -> {}", orig_pred, pred); + let pred = fcx.normalize_associated_types_in(span, &pred); + let cause = traits::ObligationCause::new( + span, + fcx.body_id, + traits::ItemObligation(trait_def_id), + ); + let obligation = traits::Obligation::new(cause, fcx.param_env, pred); + + fcx.register_predicate(obligation); + } + } + vec![] }); } diff --git a/src/test/ui/associated-types/defaults-suitability.rs b/src/test/ui/associated-types/defaults-suitability.rs new file mode 100644 index 000000000000..6a8ea3b6d42f --- /dev/null +++ b/src/test/ui/associated-types/defaults-suitability.rs @@ -0,0 +1,93 @@ +//! Checks that associated type defaults are properly validated. +//! +//! This means: +//! * Default types are wfchecked +//! * Default types are checked against where clauses on the assoc. type +//! (eg. `type Assoc: Clone = NotClone`), and also against where clauses on +//! the trait itself when possible + +// compile-fail + +#![feature(associated_type_defaults)] + +struct NotClone; + +// Assoc. type bounds must hold for the default type +trait Tr { + type Ty: Clone = NotClone; + //~^ ERROR the trait bound `NotClone: std::clone::Clone` is not satisfied +} + +// Where-clauses defined on the trait must also be considered +trait Tr2 where Self::Ty: Clone { + //~^ ERROR the trait bound `NotClone: std::clone::Clone` is not satisfied + type Ty = NotClone; +} + +// Independent of where-clauses (there are none here), default types must always be wf +trait Tr3 { + type Ty = Vec<[u8]>; + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +} + +// Involved type parameters must fulfill all bounds required by defaults that mention them +trait Foo { + type Bar: Clone = Vec; + //~^ ERROR the trait bound `T: std::clone::Clone` is not satisfied +} + +trait Bar: Sized { + // `(): Foo` might hold for some possible impls but not all. + type Assoc: Foo = (); + //~^ ERROR the trait bound `(): Foo` is not satisfied +} + +trait IsU8 {} +impl IsU8 for T {} + +// Test that mentioning the assoc. type inside where clauses works +trait C where + Vec: Clone, + Self::Assoc: IsU8, + bool: IsU8, +{ + type Assoc = u8; +} + +// Test that we get all expected errors if that default is unsuitable +trait D where + Vec: Clone, + //~^ ERROR the trait bound `NotClone: std::clone::Clone` is not satisfied + Self::Assoc: IsU8, + //~^ ERROR the trait bound `NotClone: IsU8` is not satisfied + bool: IsU8, + //~^ ERROR the trait bound `bool: IsU8` is not satisfied +{ + type Assoc = NotClone; +} + +trait Foo2 where + >::Bar: Clone, + //~^ ERROR the trait bound `>::Baz: std::clone::Clone` is not satisfied +{ + type Bar = Vec; + type Baz = T; +} + +trait Foo3 where + >::Bar: Clone, + //~^ ERROR the trait bound `>::Baz: std::clone::Clone` is not satisfied +{ + type Bar = Vec; + type Baz = T; +} + +trait Foo4 where + >::Bar: Clone, +{ + type Bar = Vec; + type Baz: Clone = T; + //~^ ERROR the trait bound `T: std::clone::Clone` is not satisfied +} + +fn main() {} diff --git a/src/test/ui/associated-types/defaults-suitability.stderr b/src/test/ui/associated-types/defaults-suitability.stderr new file mode 100644 index 000000000000..67b31b414fa9 --- /dev/null +++ b/src/test/ui/associated-types/defaults-suitability.stderr @@ -0,0 +1,177 @@ +error[E0277]: the trait bound `NotClone: std::clone::Clone` is not satisfied + --> $DIR/defaults-suitability.rs:17:14 + | +LL | type Ty: Clone = NotClone; + | ^^^^^ the trait `std::clone::Clone` is not implemented for `NotClone` + | +note: required by `Tr` + --> $DIR/defaults-suitability.rs:16:1 + | +LL | trait Tr { + | ^^^^^^^^ + +error[E0277]: the trait bound `NotClone: std::clone::Clone` is not satisfied + --> $DIR/defaults-suitability.rs:22:27 + | +LL | trait Tr2 where Self::Ty: Clone { + | ^^^^^ the trait `std::clone::Clone` is not implemented for `NotClone` + | +note: required by `Tr2` + --> $DIR/defaults-suitability.rs:22:1 + | +LL | trait Tr2 where Self::Ty: Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `T: std::clone::Clone` is not satisfied + --> $DIR/defaults-suitability.rs:35:15 + | +LL | type Bar: Clone = Vec; + | ^^^^^ the trait `std::clone::Clone` is not implemented for `T` + | + = help: consider adding a `where T: std::clone::Clone` bound + = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec` +note: required by `Foo` + --> $DIR/defaults-suitability.rs:34:1 + | +LL | trait Foo { + | ^^^^^^^^^^^^ + +error[E0277]: the trait bound `(): Foo` is not satisfied + --> $DIR/defaults-suitability.rs:41:17 + | +LL | type Assoc: Foo = (); + | ^^^^^^^^^ the trait `Foo` is not implemented for `()` + | +note: required by `Bar` + --> $DIR/defaults-suitability.rs:39:1 + | +LL | trait Bar: Sized { + | ^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `NotClone: IsU8` is not satisfied + --> $DIR/defaults-suitability.rs:61:18 + | +LL | Self::Assoc: IsU8, + | ^^^^^^^^^^^^^^^^^ the trait `IsU8` is not implemented for `NotClone` + | +note: required by `D` + --> $DIR/defaults-suitability.rs:58:1 + | +LL | / trait D where +LL | | Vec: Clone, +LL | | +LL | | Self::Assoc: IsU8, +... | +LL | | type Assoc = NotClone; +LL | | } + | |_^ + +error[E0277]: the trait bound `bool: IsU8` is not satisfied + --> $DIR/defaults-suitability.rs:63:11 + | +LL | bool: IsU8, + | ^^^^^^^^^^^^^^^^^ the trait `IsU8` is not implemented for `bool` + | +note: required by `D` + --> $DIR/defaults-suitability.rs:58:1 + | +LL | / trait D where +LL | | Vec: Clone, +LL | | +LL | | Self::Assoc: IsU8, +... | +LL | | type Assoc = NotClone; +LL | | } + | |_^ + +error[E0277]: the trait bound `NotClone: std::clone::Clone` is not satisfied + --> $DIR/defaults-suitability.rs:59:23 + | +LL | Vec: Clone, + | ^^^^^ the trait `std::clone::Clone` is not implemented for `NotClone` + | + = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec` +note: required by `D` + --> $DIR/defaults-suitability.rs:58:1 + | +LL | / trait D where +LL | | Vec: Clone, +LL | | +LL | | Self::Assoc: IsU8, +... | +LL | | type Assoc = NotClone; +LL | | } + | |_^ + +error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not satisfied + --> $DIR/defaults-suitability.rs:70:29 + | +LL | >::Bar: Clone, + | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` + | + = help: consider adding a `where >::Baz: std::clone::Clone` bound + = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` +note: required by `Foo2` + --> $DIR/defaults-suitability.rs:69:1 + | +LL | / trait Foo2 where +LL | | >::Bar: Clone, +LL | | +LL | | { +LL | | type Bar = Vec; +LL | | type Baz = T; +LL | | } + | |_^ + +error[E0277]: the trait bound `>::Baz: std::clone::Clone` is not satisfied + --> $DIR/defaults-suitability.rs:78:29 + | +LL | >::Bar: Clone, + | ^^^^^ the trait `std::clone::Clone` is not implemented for `>::Baz` + | + = help: consider adding a `where >::Baz: std::clone::Clone` bound + = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec<>::Baz>` +note: required by `Foo3` + --> $DIR/defaults-suitability.rs:77:1 + | +LL | / trait Foo3 where +LL | | >::Bar: Clone, +LL | | +LL | | { +LL | | type Bar = Vec; +LL | | type Baz = T; +LL | | } + | |_^ + +error[E0277]: the trait bound `T: std::clone::Clone` is not satisfied + --> $DIR/defaults-suitability.rs:89:15 + | +LL | type Baz: Clone = T; + | ^^^^^ the trait `std::clone::Clone` is not implemented for `T` + | + = help: consider adding a `where T: std::clone::Clone` bound +note: required by `Foo4` + --> $DIR/defaults-suitability.rs:85:1 + | +LL | / trait Foo4 where +LL | | >::Bar: Clone, +LL | | { +LL | | type Bar = Vec; +LL | | type Baz: Clone = T; +LL | | +LL | | } + | |_^ + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/defaults-suitability.rs:29:5 + | +LL | type Ty = Vec<[u8]>; + | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = note: to learn more, visit + = note: required by `std::vec::Vec` + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0277`.