From 1a27a8496c1bb710da3518d0ea27e1742531acfe Mon Sep 17 00:00:00 2001 From: kadmin Date: Tue, 22 Nov 2022 08:30:42 +0000 Subject: [PATCH] Implement unification of const abstract impls If two impls cover the entire set of possibilities: i.e. ``` impl TraitName for Struct {} impl TraitName for Struct {} ``` Then it would be expected that: ``` ... ... where Struct<{N > 1}>: TraitName { ``` Should compile. This allows for such by generating a pseudo-impl for conditions which are exhaustive. Since these impls will never be instantiated as is, it is fine to have a fake impl. For now, this checks for a single bool condition, and whether both bool implementations are present. Change some of the api surface This adds separate functionality for checking for recursive traits versus exhaustive traits. But is still a WIP. --- compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_middle/src/traits/mod.rs | 6 ++ compiler/rustc_middle/src/traits/select.rs | 5 ++ .../src/traits/structural_impls.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/project.rs | 2 + .../src/traits/select/candidate_assembly.rs | 57 +++++++++++++- .../src/traits/select/confirmation.rs | 77 ++++++++++++++++++- .../src/traits/select/mod.rs | 52 +++++++++++++ compiler/rustc_ty_utils/src/instance.rs | 1 + .../ui/const-generics/bool_cond.normal.stderr | 31 ++++++++ tests/ui/const-generics/bool_cond.rs | 50 ++++++++++++ .../const-generics/bool_must_be_exhaustive.rs | 36 +++++++++ .../bool_must_be_exhaustive.stderr | 27 +++++++ .../double_bool_cond.normal.stderr | 24 ++++++ tests/ui/const-generics/double_bool_cond.rs | 36 +++++++++ tests/ui/const-generics/inductive.rs | 14 ++++ tests/ui/const-generics/inductive.stderr | 9 +++ .../const-generics/int_must_be_exhaustive.rs | 46 +++++++++++ tests/ui/const-generics/unified_with_assoc.rs | 27 +++++++ .../const-generics/unified_with_assoc.stderr | 28 +++++++ 21 files changed, 530 insertions(+), 2 deletions(-) create mode 100644 tests/ui/const-generics/bool_cond.normal.stderr create mode 100644 tests/ui/const-generics/bool_cond.rs create mode 100644 tests/ui/const-generics/bool_must_be_exhaustive.rs create mode 100644 tests/ui/const-generics/bool_must_be_exhaustive.stderr create mode 100644 tests/ui/const-generics/double_bool_cond.normal.stderr create mode 100644 tests/ui/const-generics/double_bool_cond.rs create mode 100644 tests/ui/const-generics/inductive.rs create mode 100644 tests/ui/const-generics/inductive.stderr create mode 100644 tests/ui/const-generics/int_must_be_exhaustive.rs create mode 100644 tests/ui/const-generics/unified_with_assoc.rs create mode 100644 tests/ui/const-generics/unified_with_assoc.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index bbc3d291e2007..fb4d675f68020 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -430,6 +430,8 @@ declare_features! ( (active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None), /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), + /// Allow multiple const-generic impls to unify for traits which are abstract. + (active, impl_exhaustive_const_traits, "1.65.0", Some(104806), None), /// Allows `impl Trait` to be used inside associated types (RFC 2515). (active, impl_trait_in_assoc_type, "1.70.0", Some(63063), None), /// Allows `impl Trait` as output type in `Fn` traits in return position of functions. diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 85116555fc0ec..2ea0394531488 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -653,6 +653,8 @@ pub enum ImplSource<'tcx, N> { /// Successful resolution for a builtin impl. Builtin(BuiltinImplSource, Vec), + /// Impl Source for an Abstract Const unification. + Exhaustive(Vec), } impl<'tcx, N> ImplSource<'tcx, N> { @@ -660,6 +662,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { match self { ImplSource::UserDefined(i) => i.nested, ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n, + ImplSource::Exhaustive(n) => n, } } @@ -667,6 +670,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { match self { ImplSource::UserDefined(i) => &i.nested, ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => &n, + ImplSource::Exhaustive(n) => &n, } } @@ -674,6 +678,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { match self { ImplSource::UserDefined(i) => &mut i.nested, ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n, + ImplSource::Exhaustive(ref mut n) => n, } } @@ -691,6 +696,7 @@ impl<'tcx, N> ImplSource<'tcx, N> { ImplSource::Builtin(source, n) => { ImplSource::Builtin(source, n.into_iter().map(f).collect()) } + ImplSource::Exhaustive(n) => ImplSource::Exhaustive(n.into_iter().map(f).collect()), } } } diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index a90d58f5fc17a..c26dc594c3858 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -167,6 +167,11 @@ pub enum SelectionCandidate<'tcx> { /// Implementation of `const Destruct`, optionally from a custom `impl const Drop`. ConstDestructCandidate(Option), + + /// Candidate which is generated for a abstract const, unifying other impls if they + /// exhaustively cover all values for a type. + /// Will never actually be used, by construction. + ExhaustiveCandidate(ty::PolyTraitPredicate<'tcx>), } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index d7dc429f53b06..daafa4e80cd5f 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -16,6 +16,7 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { super::ImplSource::Param(ct, n) => { write!(f, "ImplSourceParamData({n:?}, {ct:?})") } + super::ImplSource::Exhaustive(ref n) => write!(f, "Exhaustive({:?})", n), } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d3739733c1d10..122e111ea3e75 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -815,6 +815,7 @@ symbols! { if_let_guard, if_while_or_patterns, ignore, + impl_exhaustive_const_traits, impl_header_lifetime_elision, impl_lint_pass, impl_trait_in_assoc_type, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 98f458267273d..8a454452467c4 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1926,6 +1926,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // why we special case object types. false } + ImplSource::Exhaustive(..) => false, ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // These traits have no associated types. @@ -2007,6 +2008,7 @@ fn confirm_select_candidate<'cx, 'tcx>( } ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) | ImplSource::Param(..) + | ImplSource::Exhaustive(..) | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // we don't create Select candidates with this kind of resolution diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index b9f31be25b175..8e224251135e7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -12,7 +12,7 @@ use rustc_hir as hir; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, ConstKind, Ty, TypeVisitableExt}; use crate::traits; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -118,6 +118,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_closure_candidates(obligation, &mut candidates); self.assemble_fn_pointer_candidates(obligation, &mut candidates); + self.assemble_candidates_from_exhaustive_impls(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_from_object_ty(obligation, &mut candidates); } @@ -348,6 +349,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; let obligation_args = obligation.predicate.skip_binder().trait_ref.args; + // disallow any adts to have recursive types in the LHS + if let ty::Adt(_, args) = obligation.predicate.skip_binder().self_ty().kind() { + if args.consts().any(|c| matches!(c.kind(), ConstKind::Expr(_))) { + return; + } + } self.tcx().for_each_relevant_impl( obligation.predicate.def_id(), obligation.predicate.skip_binder().trait_ref.self_ty(), @@ -465,6 +472,54 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } false } + /// When constructing an impl over a generic const enum (i.e. bool = { true, false }) + /// If all possible variants of an enum are implemented AND the obligation is over that + /// variant, + fn assemble_candidates_from_exhaustive_impls( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + if !self.tcx().features().impl_exhaustive_const_traits { + return; + } + + // see note in `assemble_candidates_from_impls`. + if obligation.predicate.references_error() { + return; + } + + // note: ow = otherwise + // - check if trait has abstract const argument(s) which is (are) enum or bool, ow return + // - check if trait enum is non-exhaustive, ow return + // - construct required set of possible combinations, with false unless present + // for each relevant trait + // - check if is same trait + // - set combo as present + // If all required sets are present, add candidate impl generic over all combinations. + + let query = obligation.predicate.skip_binder().self_ty(); + let ty::Adt(_adt_def, adt_substs) = query.kind() else { + return; + }; + + let Some(ct) = adt_substs + .consts() + .filter(|ct| { + matches!(ct.kind(), ty::ConstKind::Unevaluated(..) | ty::ConstKind::Param(_)) + }) + .next() + else { + return; + }; + + // explicitly gate certain types which are exhaustive + if !super::exhaustive_types(self.tcx(), ct.ty(), |_| {}) { + return; + } + + candidates.vec.push(ExhaustiveCandidate(obligation.predicate)); + } fn assemble_candidates_from_auto_impls( &mut self, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 7140fedb74a3e..1957f14a66963 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -14,7 +14,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch}; use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, - TraitPredicate, Ty, TyCtxt, TypeVisitableExt, + TraitPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitableExt, }; use rustc_span::def_id::DefId; @@ -120,6 +120,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let data = self.confirm_const_destruct_candidate(obligation, def_id)?; ImplSource::Builtin(BuiltinImplSource::Misc, data) } + ExhaustiveCandidate(candidate) => { + let obligations = self.confirm_exhaustive_candidate(obligation, candidate); + ImplSource::Exhaustive(obligations) + } }; // The obligations returned by confirmation are recursively evaluated @@ -1360,4 +1364,75 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(nested) } + + /// Generates obligations for a lazy candidate + /// Where the obligations generated are all possible values for the type of a + /// `ConstKind::Unevaluated(..)`. + /// In the future, it would be nice to extend this to inductive proofs. + #[allow(unused_variables, unused_mut, dead_code)] + fn confirm_exhaustive_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidate: ty::PolyTraitPredicate<'tcx>, + ) -> Vec> { + let mut obligations = vec![]; + let tcx = self.tcx(); + + let query = obligation.predicate.skip_binder().trait_ref.self_ty(); + let ty::Adt(_adt_def, adt_substs) = query.kind() else { + return obligations; + }; + // Find one adt subst at a time, then will be handled recursively. + let const_to_replace = adt_substs + .consts() + .find(|ct| { + matches!(ct.kind(), ty::ConstKind::Unevaluated(..) | ty::ConstKind::Param(_)) + }) + .unwrap(); + let is_exhaustive = super::exhaustive_types(tcx, const_to_replace.ty(), |val| { + let predicate = candidate.map_bound(|pt_ref| { + let query = pt_ref.self_ty(); + let mut folder = Folder { tcx, replace: const_to_replace, with: val }; + let mut new_poly_trait_ref = pt_ref.clone(); + new_poly_trait_ref.trait_ref = TraitRef::new( + tcx, + pt_ref.trait_ref.def_id, + [query.fold_with(&mut folder).into()] + .into_iter() + .chain(pt_ref.trait_ref.args.iter().skip(1)), + ); + new_poly_trait_ref + }); + + let ob = Obligation::new( + self.tcx(), + obligation.cause.clone(), + obligation.param_env, + predicate, + ); + obligations.push(ob); + }); + + // should only allow exhaustive types in + // candidate_assembly::assemble_candidates_from_exhaustive_impls + assert!(is_exhaustive); + + obligations + } +} + +/// Folder for replacing specific const values in `substs`. +struct Folder<'tcx> { + tcx: TyCtxt<'tcx>, + replace: ty::Const<'tcx>, + with: ty::Const<'tcx>, +} + +impl<'tcx> TypeFolder> for Folder<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + if c == self.replace { self.with } else { c } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c29696bc817f7..f4ed0aa267bbd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1806,6 +1806,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. match (&other.candidate, &victim.candidate) { + (_, ExhaustiveCandidate(..)) => DropVictim::Yes, + (ExhaustiveCandidate(..), _) => DropVictim::No, // FIXME(@jswrenn): this should probably be more sophisticated (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, @@ -2604,6 +2606,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { assert_eq!(predicates.parent, None); let predicates = predicates.instantiate_own(tcx, args); let mut obligations = Vec::with_capacity(predicates.len()); + for (index, (predicate, span)) in predicates.into_iter().enumerate() { let cause = if Some(parent_trait_pred.def_id()) == tcx.lang_items().coerce_unsized_trait() { @@ -2999,3 +3002,52 @@ fn bind_generator_hidden_types_above<'tcx>( )); ty::Binder::bind_with_vars(hidden_types, bound_vars) } + +// For a given type, will run a function over all constants for that type if permitted. +// returns false if not permitted. Callers should not rely on the order. +fn exhaustive_types<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + mut f: impl FnMut(ty::Const<'tcx>), +) -> bool { + use std::mem::transmute; + match ty.kind() { + ty::Bool => { + for v in [true, false].into_iter() { + f(ty::Const::from_bool(tcx, v)); + } + } + // Should always compile, as this is never instantiable + ty::Never => {} + ty::Adt(adt_def, _substs) => { + if adt_def.is_payloadfree() { + return true; + } + if adt_def.is_variant_list_non_exhaustive() { + return false; + } + + // FIXME(julianknodt): here need to create constants for each variant + return false; + } + + ty::Int(ty::IntTy::I8) => { + for v in -128i8..127i8 { + let c = ty::Const::from_bits( + tcx, + unsafe { transmute(v as i128) }, + ty::ParamEnv::empty().and(ty), + ); + f(c); + } + } + ty::Uint(ty::UintTy::U8) => { + for v in 0u8..=255u8 { + let c = ty::Const::from_bits(tcx, v as u128, ty::ParamEnv::empty().and(ty)); + f(c); + } + } + _ => return false, + } + true +} diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index a21b5ef05e60b..4d8e0083ca399 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -295,6 +295,7 @@ fn resolve_associated_item<'tcx>( } traits::ImplSource::Param(..) | traits::ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | traits::ImplSource::Exhaustive(..) | traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => None, }) } diff --git a/tests/ui/const-generics/bool_cond.normal.stderr b/tests/ui/const-generics/bool_cond.normal.stderr new file mode 100644 index 0000000000000..87915c1b790cc --- /dev/null +++ b/tests/ui/const-generics/bool_cond.normal.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `ConstOption: Default` is not satisfied + --> $DIR/bool_cond.rs:42:5 + | +LL | #[derive(Default)] + | ------- in this derive macro expansion +... +LL | _a: ConstOption, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `ConstOption` + | + = help: the following other types implement trait `Default`: + ConstOption + ConstOption + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `ConstOption: Default` is not satisfied + --> $DIR/bool_cond.rs:44:5 + | +LL | #[derive(Default)] + | ------- in this derive macro expansion +... +LL | _b: ConstOption, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `ConstOption` + | + = help: the following other types implement trait `Default`: + ConstOption + ConstOption + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/bool_cond.rs b/tests/ui/const-generics/bool_cond.rs new file mode 100644 index 0000000000000..4abe6e99a5db8 --- /dev/null +++ b/tests/ui/const-generics/bool_cond.rs @@ -0,0 +1,50 @@ +// gate-test-impl_exhaustive_const_traits +// [unified] run-pass +// revisions: unified normal +#![feature(generic_const_exprs)] +#![cfg_attr(unified, feature(impl_exhaustive_const_traits))] +#![allow(incomplete_features)] + +pub const fn bool_to_usize(b: bool) -> usize { + b as usize +} + +pub struct ConstOption where [T; bool_to_usize(S)]:, { + _v: [T; bool_to_usize(S)] +} + +impl Default for ConstOption { + fn default() -> Self { + Self { + _v: [T::default()] + } + } +} + +impl Default for ConstOption { + fn default() -> Self { + Self { + _v: [], + } + } +} + +fn _test_func() where ConstOption= 5}>: Default, + [usize; bool_to_usize(N >= 5)]: { + +} + +#[derive(Default)] +pub struct Arg where + [(); bool_to_usize(N <= 0)]:, [(); bool_to_usize(N <= 1)]:, + [(); bool_to_usize(N <= 5)]:, [(); bool_to_usize(N > 3)]:, + { + _a: ConstOption, + //[normal]~^ ERROR: the trait + _b: ConstOption, + //[normal]~^ ERROR: the trait +} + +fn main() { + let _def = Arg::<2>::default(); +} diff --git a/tests/ui/const-generics/bool_must_be_exhaustive.rs b/tests/ui/const-generics/bool_must_be_exhaustive.rs new file mode 100644 index 0000000000000..bc79dcae62fd7 --- /dev/null +++ b/tests/ui/const-generics/bool_must_be_exhaustive.rs @@ -0,0 +1,36 @@ +#![feature(generic_const_exprs)] +#![feature(impl_exhaustive_const_traits)] +#![allow(incomplete_features)] + +pub const fn bool_to_usize(b: bool) -> usize { + b as usize +} + +pub struct ConstOption where [T; bool_to_usize(S)]:, { + _v: [T; bool_to_usize(S)] +} + +impl Default for ConstOption { + fn default() -> Self { + Self { + _v: [T::default()] + } + } +} + +fn _test_func() where ConstOption= 5}>: Default, + [usize; bool_to_usize(N >= 5)]: { + +} + +#[derive(Default)] +pub struct Arg where [(); bool_to_usize(N <= 0)]:, [(); bool_to_usize(N <= 1)]:, { + _a: ConstOption, + //~^ ERROR trait bound + _b: ConstOption, + //~^ ERROR trait bound +} + +fn main() { + let _def = Arg::<2>::default(); +} diff --git a/tests/ui/const-generics/bool_must_be_exhaustive.stderr b/tests/ui/const-generics/bool_must_be_exhaustive.stderr new file mode 100644 index 0000000000000..c6aea4351ec08 --- /dev/null +++ b/tests/ui/const-generics/bool_must_be_exhaustive.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `ConstOption: Default` is not satisfied + --> $DIR/bool_must_be_exhaustive.rs:28:5 + | +LL | #[derive(Default)] + | ------- in this derive macro expansion +LL | pub struct Arg where [(); bool_to_usize(N <= 0)]:, [(); bool_to_usize(N <= 1)]:, { +LL | _a: ConstOption, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `ConstOption` + | + = help: the trait `Default` is implemented for `ConstOption` + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `ConstOption: Default` is not satisfied + --> $DIR/bool_must_be_exhaustive.rs:30:5 + | +LL | #[derive(Default)] + | ------- in this derive macro expansion +... +LL | _b: ConstOption, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `ConstOption` + | + = help: the trait `Default` is implemented for `ConstOption` + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/double_bool_cond.normal.stderr b/tests/ui/const-generics/double_bool_cond.normal.stderr new file mode 100644 index 0000000000000..f0cb3f76ecc3f --- /dev/null +++ b/tests/ui/const-generics/double_bool_cond.normal.stderr @@ -0,0 +1,24 @@ +error[E0277]: the trait bound `ConstTwo<{ N <= 5 }, {N > 3}>: Default` is not satisfied + --> $DIR/double_bool_cond.rs:30:5 + | +LL | #[derive(Default)] + | ------- in this derive macro expansion +... +LL | _c: ConstTwo<{ N <= 5 }, {N > 3}>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `ConstTwo<{ N <= 5 }, {N > 3}>` + | + = help: the following other types implement trait `Default`: + ConstTwo + ConstTwo + ConstTwo + ConstTwo + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `ConstTwo<{ N <= 5 }, {N > 3}>` with `#[derive(Default)]` + | +LL + #[derive(Default)] +LL | pub struct ConstTwo; + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/double_bool_cond.rs b/tests/ui/const-generics/double_bool_cond.rs new file mode 100644 index 0000000000000..c4c51318ce924 --- /dev/null +++ b/tests/ui/const-generics/double_bool_cond.rs @@ -0,0 +1,36 @@ +// [unified] run-pass +// revisions: unified normal +#![feature(generic_const_exprs)] +#![cfg_attr(unified, feature(impl_exhaustive_const_traits))] +#![allow(incomplete_features)] + +pub const fn bool_to_usize(b: bool) -> usize { + b as usize +} + +pub struct ConstTwo; + +impl Default for ConstTwo { + fn default() -> Self { Self } +} +impl Default for ConstTwo { + fn default() -> Self { Self } +} +impl Default for ConstTwo { + fn default() -> Self { Self } +} +impl Default for ConstTwo { + fn default() -> Self { Self } +} + +#[derive(Default)] +pub struct Arg where + [(); bool_to_usize(N <= 5)]:, [(); bool_to_usize(N > 3)]:, + { + _c: ConstTwo<{ N <= 5 }, {N > 3}>, + //[normal]~^ ERROR: the trait +} + +fn main() { + let _def = Arg::<2>::default(); +} diff --git a/tests/ui/const-generics/inductive.rs b/tests/ui/const-generics/inductive.rs new file mode 100644 index 0000000000000..477b5758b44e1 --- /dev/null +++ b/tests/ui/const-generics/inductive.rs @@ -0,0 +1,14 @@ +// check-fail +#![allow(incomplete_features)] +#![feature(impl_exhaustive_const_traits)] +#![feature(generic_const_exprs)] + +trait Dot {} + +struct Mat; + +impl Dot for Mat<0> {} +impl Dot for Mat where Mat<{N-1}>: Dot {} +//~^ ERROR: evaluation of + +fn main() {} diff --git a/tests/ui/const-generics/inductive.stderr b/tests/ui/const-generics/inductive.stderr new file mode 100644 index 0000000000000..372d3ab87d55c --- /dev/null +++ b/tests/ui/const-generics/inductive.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of ` as Dot>::{constant#0}` failed + --> $DIR/inductive.rs:11:48 + | +LL | impl Dot for Mat where Mat<{N-1}>: Dot {} + | ^^^ attempt to compute `0_usize - 1_usize`, which would overflow + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/const-generics/int_must_be_exhaustive.rs b/tests/ui/const-generics/int_must_be_exhaustive.rs new file mode 100644 index 0000000000000..3d83a7f7052b1 --- /dev/null +++ b/tests/ui/const-generics/int_must_be_exhaustive.rs @@ -0,0 +1,46 @@ +// check-pass +#![feature(generic_const_exprs)] +#![feature(impl_exhaustive_const_traits)] +#![allow(incomplete_features)] + +struct Val {} + +macro_rules! u8_impl { + ($( $v: expr ),*) => { + $( + impl Default for Val<$v> { + fn default() -> Self { Self {} } + } + )* + } +} + +u8_impl!( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +); + +pub const fn wrapping_u8_add(v: u8) -> u8 { + v.wrapping_add(1) +} + +#[derive(Default)] +struct Example { + v: Val, +} + +fn main() {} diff --git a/tests/ui/const-generics/unified_with_assoc.rs b/tests/ui/const-generics/unified_with_assoc.rs new file mode 100644 index 0000000000000..7810768617434 --- /dev/null +++ b/tests/ui/const-generics/unified_with_assoc.rs @@ -0,0 +1,27 @@ +#![feature(generic_const_exprs)] +#![feature(impl_exhaustive_const_traits)] +#![allow(incomplete_features)] + +pub trait WithAssoc { + const VAL: usize; + type Item; +} + +pub struct Demo; + +impl WithAssoc for Demo { + const VAL: usize = 3; + type Item = usize; +} +impl WithAssoc for Demo { + const VAL: usize = 5; + type Item = String; +} +fn demo_func() where Demo<{ N > 5 }>: WithAssoc, + [();Demo::<{N>5}>::VAL]:, + //~^ ERROR cycle detected + { + let _ = [0; Demo::<{N>5}>::VAL]; +} + +fn main() {} diff --git a/tests/ui/const-generics/unified_with_assoc.stderr b/tests/ui/const-generics/unified_with_assoc.stderr new file mode 100644 index 0000000000000..9aa8608e1d205 --- /dev/null +++ b/tests/ui/const-generics/unified_with_assoc.stderr @@ -0,0 +1,28 @@ +error[E0391]: cycle detected when building an abstract representation for `demo_func::{constant#1}` + --> $DIR/unified_with_assoc.rs:21:7 + | +LL | [();Demo::<{N>5}>::VAL]:, + | ^^^^^^^^^^^^^^^^^^ + | +note: ...which requires building THIR for `demo_func::{constant#1}`... + --> $DIR/unified_with_assoc.rs:21:7 + | +LL | [();Demo::<{N>5}>::VAL]:, + | ^^^^^^^^^^^^^^^^^^ +note: ...which requires type-checking `demo_func::{constant#1}`... + --> $DIR/unified_with_assoc.rs:21:7 + | +LL | [();Demo::<{N>5}>::VAL]:, + | ^^^^^^^^^^^^^^^^^^ + = note: ...which again requires building an abstract representation for `demo_func::{constant#1}`, completing the cycle +note: cycle used when checking that `demo_func` is well-formed + --> $DIR/unified_with_assoc.rs:20:1 + | +LL | / fn demo_func() where Demo<{ N > 5 }>: WithAssoc, +LL | | [();Demo::<{N>5}>::VAL]:, + | |___________________________^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`.