diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9b582eeb2c520..121ac0a2f332d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1046,6 +1046,11 @@ impl<'tcx> rustc_type_ir::inherent::ParamEnv> for ParamEnv<'tcx> { fn caller_bounds(self) -> impl inherent::SliceLike> { self.caller_bounds() } + + #[inline] + fn empty() -> Self { + Self::empty() + } } impl<'tcx> ParamEnv<'tcx> { diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index c3ccb46069063..808bc6374392a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1282,19 +1282,58 @@ where Ok(()) } - // Try to evaluate a const, or return `None` if the const is too generic. - // This doesn't mean the const isn't evaluatable, though, and should be treated - // as an ambiguity rather than no-solution. + // Try to evaluate a const, returning `(Option, Certainty)`, this returns + // `NoSolution` when concrete const fails to prove well-formedness in empty env. + // + // `(None, Maybe(_))` means we couldn't fully check const's well-formedness, and + // bailed without evaluation. + // + // `(None, Certainty::Yes)` means const is too generic, and doesn't indicate the + // const isn't evaluatable, should be treated as an ambiguity rather than no-solution. pub(super) fn evaluate_const( &mut self, param_env: I::ParamEnv, alias_const: ty::AliasConst, - ) -> Result, RerunNonErased> { + ) -> Result<(Option, Certainty), NoSolutionOrRerunNonErased> { if self.typing_mode().is_erased_not_coherence() { match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {} } + let cx = self.cx(); + + // Checking in empty env to be sure const is WF without relying on assumptions from env, + // preventing ill-formed consts from reaching CTFE. + // + // For example without this check const with `[u8]: Sized` bound could get to be evaluated + // in environment with the same assumption, pass and ICE later in CTFE. + let certainty = if alias_const.has_non_region_infer() + || alias_const.has_non_region_param() + || alias_const.has_non_region_placeholders() + { + // Skip proving for not fully concrete consts as they either won't reach CTFE or + // can't depend on generics even if they have them to pass CTFE. + Certainty::Yes + } + // We do this only for GCA consts. Doing this check for GCE introduces cycles with anon + // consts in impl blocks. + else if cx.features().generic_const_args() { + // FIXME(zedddie): we should check this in `fully_monomorphized()` Typing mode. + let goal = Goal::new( + cx, + ParamEnv::empty(), + ty::ClauseKind::WellFormed(alias_const.to_const(cx, ty::IsRigid::Yes).into()), + ); + self.add_goal(GoalSource::AliasWellFormed, goal)?; + let certainty = self.try_evaluate_added_goals()?; + + if matches!(certainty, Certainty::Maybe(_)) { + return Ok((None, certainty)); + } + certainty + } else { + Certainty::Yes + }; - Ok(self.delegate.evaluate_const(param_env, alias_const)) + Ok((self.delegate.evaluate_const(param_env, alias_const), certainty)) } pub(super) fn evaluate_const_and_instantiate_projection_term( @@ -1305,11 +1344,11 @@ where alias_const: ty::AliasConst, ) -> QueryResultOrRerunNonErased { match self.evaluate_const(param_env, alias_const)? { - Some(evaluated) => { + (Some(evaluated), _) => { self.eq(param_env, expected_term, evaluated.into())?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - None if self.cx().features().generic_const_args() => { + (None, certainty) if self.cx().features().generic_const_args() => { // HACK(khyperia): calling `resolve_vars_if_possible` here shouldn't be necessary, // `try_evaluate_const` calls `resolve_vars_if_possible` already. However, we want // to check `has_non_region_infer` against the type with vars resolved (i.e. check @@ -1331,10 +1370,10 @@ where projection_term.to_term(self.cx(), ty::IsRigid::Yes), expected_term, )?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + self.evaluate_added_goals_and_make_canonical_response(certainty) } } - None => { + (None, _) => { // Legacy behavior: always treat as ambiguous self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 195f08dfafd59..ec04732e92eb0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -212,13 +212,19 @@ where // error in that case is unnecessary noise. This may change in the future once // evaluation failures are allowed to impact selection, e.g. generic const // expressions in impl headers or `where`-clauses. + // + // `evaluate_const` itself can return `NoSolution` if alias' WF doesn't hold in + // empty environment. // FIXME(generic_const_exprs): Implement handling for generic // const expressions here. - if let Some(_normalized) = self.evaluate_const(param_env, alias_const)? { - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + match self.evaluate_const(param_env, alias_const)? { + (None, certainty) => self.evaluate_added_goals_and_make_canonical_response( + certainty.and(Certainty::AMBIGUOUS), + ), + (Some(_normalized), _) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } } } diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 29c65974d8b28..a48ad4cfe1ac8 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -9,6 +9,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; +use crate::inherent::*; use crate::{self as ty, AliasConst, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. @@ -84,6 +85,10 @@ impl AliasConst { AliasConst { kind, args, _use_alias_new_instead: () } } + pub fn to_const(self, interner: I, is_rigid: ty::IsRigid) -> I::Const { + I::Const::new_alias(interner, is_rigid, self) + } + pub fn type_of(self, interner: I) -> ty::Unnormalized { let def_id = match self.kind { ty::AliasConstKind::Projection { def_id } => def_id.into(), diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 4776771f5e7f8..97ba6891c7e95 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -619,6 +619,14 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { #[rust_analyzer::prefer_underscore_import] pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { fn caller_bounds(self) -> impl SliceLike; + + /// Construct a trait environment suitable for contexts where there are + /// no where-clauses in scope. In the majority of cases it is incorrect + /// to use an empty environment. See the [dev guide section][param_env_guide] + /// for information on what a `ParamEnv` is and how to acquire one. + /// + /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/typing_parameter_envs.html + fn empty() -> Self; } #[rust_analyzer::prefer_underscore_import] diff --git a/tests/ui/const-generics/gca/impl-block-type-const-type-mismatch.rs b/tests/ui/const-generics/gca/impl-block-type-const-type-mismatch.rs new file mode 100644 index 0000000000000..a0731e8daf277 --- /dev/null +++ b/tests/ui/const-generics/gca/impl-block-type-const-type-mismatch.rs @@ -0,0 +1,13 @@ +//! Regression test for . +//@ compile-flags: -Znext-solver=globally +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![feature(generic_const_items)] +#![feature(macroless_generic_const_args)] + +const ADD1: usize = N + 1; +type const A: usize = ADD1::; +impl [(); A::<1f64>] {} //~ ERROR: type mismatch resolving `A<1f64> == _` [E0271] +//~| ERROR: cannot define inherent `impl` for primitive types [E0390] + //~^^ ERROR: the type `[(); A::<1f64>]` is not well-formed +fn main() {} diff --git a/tests/ui/const-generics/gca/impl-block-type-const-type-mismatch.stderr b/tests/ui/const-generics/gca/impl-block-type-const-type-mismatch.stderr new file mode 100644 index 0000000000000..d274f9e233121 --- /dev/null +++ b/tests/ui/const-generics/gca/impl-block-type-const-type-mismatch.stderr @@ -0,0 +1,24 @@ +error[E0271]: type mismatch resolving `A<1f64> == _` + --> $DIR/impl-block-type-const-type-mismatch.rs:10:1 + | +LL | impl [(); A::<1f64>] {} + | ^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error: the type `[(); A::<1f64>]` is not well-formed + --> $DIR/impl-block-type-const-type-mismatch.rs:10:6 + | +LL | impl [(); A::<1f64>] {} + | ^^^^^^^^^^^^^^^ + +error[E0390]: cannot define inherent `impl` for primitive types + --> $DIR/impl-block-type-const-type-mismatch.rs:10:1 + | +LL | impl [(); A::<1f64>] {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an extension trait instead + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0271, E0390. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/const-generics/gca/rp-type-const-type-mismatch.rs b/tests/ui/const-generics/gca/rp-type-const-type-mismatch.rs new file mode 100644 index 0000000000000..121cafe8c22d9 --- /dev/null +++ b/tests/ui/const-generics/gca/rp-type-const-type-mismatch.rs @@ -0,0 +1,12 @@ +//! Regression test for . +//@ compile-flags: -Znext-solver=globally +#![feature(generic_const_items,min_generic_const_args)] +#![feature(macroless_generic_const_args, generic_const_args)] +const ADD1: usize = N + 1; +fn a() -> [usize; ADD1::] {} //~ ERROR: type mismatch resolving `ADD1<*b""> == _` [E0271] +//~| ERROR: the type `[usize; ADD1::<*b"">]` is not well-formed +//~| ERROR: mismatched types [E0308] +//~| ERROR: type mismatch resolving `ADD1<*b""> == _` [E0271] + + +fn main() {} diff --git a/tests/ui/const-generics/gca/rp-type-const-type-mismatch.stderr b/tests/ui/const-generics/gca/rp-type-const-type-mismatch.stderr new file mode 100644 index 0000000000000..86ff0f2facccd --- /dev/null +++ b/tests/ui/const-generics/gca/rp-type-const-type-mismatch.stderr @@ -0,0 +1,32 @@ +error[E0271]: type mismatch resolving `ADD1<*b""> == _` + --> $DIR/rp-type-const-type-mismatch.rs:6:11 + | +LL | fn a() -> [usize; ADD1::] {} + | ^^^^^^^^^^^^^^^^^^^^ types differ + +error: the type `[usize; ADD1::<*b"">]` is not well-formed + --> $DIR/rp-type-const-type-mismatch.rs:6:11 + | +LL | fn a() -> [usize; ADD1::] {} + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/rp-type-const-type-mismatch.rs:6:11 + | +LL | fn a() -> [usize; ADD1::] {} + | - ^^^^^^^^^^^^^^^^^^^^ expected `[usize; _]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error[E0271]: type mismatch resolving `ADD1<*b""> == _` + --> $DIR/rp-type-const-type-mismatch.rs:6:11 + | +LL | fn a() -> [usize; ADD1::] {} + | ^^^^^^^^^^^^^^^^^^^^ types differ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/const-generics/gca/type-const-arg-type-mismatch-1.rs b/tests/ui/const-generics/gca/type-const-arg-type-mismatch-1.rs new file mode 100644 index 0000000000000..42620ca444389 --- /dev/null +++ b/tests/ui/const-generics/gca/type-const-arg-type-mismatch-1.rs @@ -0,0 +1,11 @@ +//! Regression test for . +//@ compile-flags: -Znext-solver=globally +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![feature(generic_const_items)] + +const ADD1: usize = N + 1; +type const ONE: usize = ADD1::; //~ ERROR type mismatch resolving +//~| ERROR the constant `*b""` is not of type `usize` +//~| ERROR the constant `ADD1::<*b"">` is not of type `usize` +fn main() {} diff --git a/tests/ui/const-generics/gca/type-const-arg-type-mismatch-1.stderr b/tests/ui/const-generics/gca/type-const-arg-type-mismatch-1.stderr new file mode 100644 index 0000000000000..3ecedd7a4353b --- /dev/null +++ b/tests/ui/const-generics/gca/type-const-arg-type-mismatch-1.stderr @@ -0,0 +1,27 @@ +error[E0271]: type mismatch resolving `ADD1<*b""> == _` + --> $DIR/type-const-arg-type-mismatch-1.rs:8:1 + | +LL | type const ONE: usize = ADD1::; + | ^^^^^^^^^^^^^^^^^^^^^ types differ + +error: the constant `*b""` is not of type `usize` + --> $DIR/type-const-arg-type-mismatch-1.rs:8:1 + | +LL | type const ONE: usize = ADD1::; + | ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `[u8; 0]` + | +note: required by a const generic parameter in `ADD1` + --> $DIR/type-const-arg-type-mismatch-1.rs:7:12 + | +LL | const ADD1: usize = N + 1; + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ADD1` + +error: the constant `ADD1::<*b"">` is not of type `usize` + --> $DIR/type-const-arg-type-mismatch-1.rs:8:1 + | +LL | type const ONE: usize = ADD1::; + | ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found a different `usize` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/const-generics/gca/type-const-arg-type-mismatch-2.rs b/tests/ui/const-generics/gca/type-const-arg-type-mismatch-2.rs new file mode 100644 index 0000000000000..e4d7e20fcfce4 --- /dev/null +++ b/tests/ui/const-generics/gca/type-const-arg-type-mismatch-2.rs @@ -0,0 +1,12 @@ +//! Regression test for . +//@ compile-flags: -Znext-solver=globally +#![feature(min_generic_const_args)] +#![feature(macroless_generic_const_args)] +#![feature(generic_const_args)] +#![feature(generic_const_items)] + +const ADD1: usize = N + 1; +type const ONE: usize = ADD1::; //~ ERROR type mismatch resolving +//~| ERROR the constant `*b""` is not of type `usize` +//~| ERROR the constant `ADD1::<*b"">` is not of type `usize` +fn main() {} diff --git a/tests/ui/const-generics/gca/type-const-arg-type-mismatch-2.stderr b/tests/ui/const-generics/gca/type-const-arg-type-mismatch-2.stderr new file mode 100644 index 0000000000000..0dbab7e09a7ce --- /dev/null +++ b/tests/ui/const-generics/gca/type-const-arg-type-mismatch-2.stderr @@ -0,0 +1,27 @@ +error[E0271]: type mismatch resolving `ADD1<*b""> == _` + --> $DIR/type-const-arg-type-mismatch-2.rs:9:1 + | +LL | type const ONE: usize = ADD1::; + | ^^^^^^^^^^^^^^^^^^^^^ types differ + +error: the constant `*b""` is not of type `usize` + --> $DIR/type-const-arg-type-mismatch-2.rs:9:1 + | +LL | type const ONE: usize = ADD1::; + | ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `[u8; 0]` + | +note: required by a const generic parameter in `ADD1` + --> $DIR/type-const-arg-type-mismatch-2.rs:8:12 + | +LL | const ADD1: usize = N + 1; + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ADD1` + +error: the constant `ADD1::<*b"">` is not of type `usize` + --> $DIR/type-const-arg-type-mismatch-2.rs:9:1 + | +LL | type const ONE: usize = ADD1::; + | ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found a different `usize` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0271`.