generic_const_args: allow paths to non type consts

This commit is contained in:
khyperia
2026-05-05 17:35:17 +02:00
parent 4feb7221f4
commit cb2c5fc540
38 changed files with 568 additions and 46 deletions
@@ -464,6 +464,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
check_incompatible_features(sess, features);
check_dependent_features(sess, features);
check_new_solver_banned_features(sess, features);
check_features_requiring_new_solver(sess, features);
let mut visitor = PostExpansionVisitor { sess, features };
@@ -739,3 +740,25 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
});
}
}
fn check_features_requiring_new_solver(sess: &Session, features: &Features) {
if sess.opts.unstable_opts.next_solver.globally {
return;
}
// Require the new solver with GCA, because the old solver can't implement GCA correctly as it
// does not support normalization obligations for free and inherent consts.
if let Some(gca_span) = features
.enabled_lang_features()
.iter()
.find(|feat| feat.gate_name == sym::generic_const_args)
.map(|feat| feat.attr_sp)
{
#[allow(rustc::symbol_intern_string_literal)]
sess.dcx().emit_err(errors::MissingDependentFeatures {
parent_span: gca_span,
parent: sym::generic_const_args,
missing: String::from("-Znext-solver=globally"),
});
}
}
@@ -3064,7 +3064,14 @@ fn require_type_const_attribute(
span: Span,
) -> Result<(), ErrorGuaranteed> {
let tcx = self.tcx();
if tcx.is_type_const(def_id) {
// FIXME(gca): Intentionally disallowing paths to inherent associated non-type constants
// until a refactoring for how generic args for IACs are represented has been landed.
let is_inherent_assoc_const = tcx.def_kind(def_id)
== DefKind::AssocConst { is_type_const: false }
&& tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false };
if tcx.is_type_const(def_id)
|| tcx.features().generic_const_args() && !is_inherent_assoc_const
{
Ok(())
} else {
let mut err = self.dcx().struct_span_err(
+4
View File
@@ -106,6 +106,10 @@ fn generic_const_exprs(self) -> bool {
self.generic_const_exprs()
}
fn generic_const_args(self) -> bool {
self.generic_const_args()
}
fn coroutine_clone(self) -> bool {
self.coroutine_clone()
}
@@ -172,6 +172,9 @@ fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
self.type_of_opaque_hir_typeck(def_id)
}
fn is_type_const(self, def_id: DefId) -> bool {
self.is_type_const(def_id)
}
fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
self.const_of_item(def_id)
}
@@ -1196,6 +1196,44 @@ pub(super) fn evaluate_const(
self.delegate.evaluate_const(param_env, uv)
}
pub(super) fn evaluate_const_and_instantiate_normalizes_to_term(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
uv: ty::UnevaluatedConst<I>,
) -> QueryResult<I> {
match self.evaluate_const(goal.param_env, uv) {
Some(evaluated) => {
self.instantiate_normalizes_to_term(goal, evaluated.into());
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
None 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
// if there are vars we failed to resolve), so we need to call it again here.
// Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics and
// HasInfers or something, make evaluate_const return that, and make this branch be
// based on that, rather than checking `has_non_region_infer`.
if self.resolve_vars_if_possible(uv).has_non_region_infer() {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
} else {
// We do not instantiate to the `uv` passed in, but rather
// `goal.predicate.alias`. The `uv` passed in might correspond to the `impl`
// form of a constant (with generic arguments corresponding to the impl block),
// however, we want to structurally instantiate to the original, non-rebased,
// trait `Self` form of the constant (with generic arguments being the trait
// `Self` type).
self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
None => {
// Legacy behavior: always treat as ambiguous
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
}
pub(super) fn is_transmutable(
&mut self,
src: I::Ty,
@@ -2,7 +2,7 @@
use tracing::instrument;
use crate::delegate::SolverDelegate;
use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
use crate::solve::{EvalCtxt, Goal, QueryResult};
impl<D, I> EvalCtxt<'_, D>
where
@@ -14,17 +14,7 @@ pub(super) fn normalize_anon_const(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
) -> QueryResult<I> {
if let Some(normalized_const) = self.evaluate_const(
goal.param_env,
ty::UnevaluatedConst::new(
goal.predicate.alias.def_id().try_into().unwrap(),
goal.predicate.alias.args,
),
) {
self.instantiate_normalizes_to_term(goal, normalized_const.into());
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
let uv = goal.predicate.alias.expect_ct(self.cx());
self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv)
}
}
@@ -30,13 +30,20 @@ pub(super) fn normalize_free_alias(
.map(|pred| goal.with(cx, pred)),
);
let actual = if free_alias.kind(cx).is_type() {
cx.type_of(free_alias.def_id()).instantiate(cx, free_alias.args).skip_norm_wip().into()
} else {
cx.const_of_item(free_alias.def_id())
.instantiate(cx, free_alias.args)
.skip_norm_wip()
.into()
let actual = match free_alias.kind(cx) {
ty::AliasTermKind::FreeTy { def_id } => {
cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into()
}
ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id) => {
cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into()
}
ty::AliasTermKind::FreeConst { .. } => {
return self.evaluate_const_and_instantiate_normalizes_to_term(
goal,
free_alias.expect_ct(cx),
);
}
kind => panic!("expected free alias, found {kind:?}"),
};
self.instantiate_normalizes_to_term(goal, actual);
@@ -52,13 +52,23 @@ pub(super) fn normalize_inherent_associated_term(
.map(|pred| goal.with(cx, pred)),
);
let normalized = if inherent.kind(cx).is_type() {
cx.type_of(inherent.def_id()).instantiate(cx, inherent_args).skip_norm_wip().into()
} else {
cx.const_of_item(inherent.def_id())
.instantiate(cx, inherent_args)
.skip_norm_wip()
.into()
let normalized = match inherent.kind(cx) {
ty::AliasTermKind::InherentTy { def_id } => {
cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into()
}
ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id) => {
cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into()
}
ty::AliasTermKind::InherentConst { .. } => {
// FIXME(gca): This is dead code at the moment. It should eventually call
// self.evaluate_const like projected consts do in consider_impl_candidate in
// normalizes_to/mod.rs. However, how generic args are represented for IACs is up in
// the air right now.
// Will self.evaluate_const eventually take the inherent_args or the impl_args form
// of args? It might be either.
panic!("References to inherent associated consts should have been blocked");
}
kind => panic!("expected inherent alias, found {kind:?}"),
};
self.instantiate_normalizes_to_term(goal, normalized);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
@@ -383,19 +383,30 @@ fn consider_impl_candidate(
// Finally we construct the actual value of the associated type.
let term = match goal.predicate.alias.kind(cx) {
ty::AliasTermKind::ProjectionTy { .. } => {
cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
ty::AliasTermKind::ProjectionTy { .. } => cx
.type_of(target_item_def_id)
.instantiate(cx, target_args)
.skip_norm_wip()
.into(),
ty::AliasTermKind::ProjectionConst { .. }
if cx.is_type_const(target_item_def_id) =>
{
cx.const_of_item(target_item_def_id)
.instantiate(cx, target_args)
.skip_norm_wip()
.into()
}
ty::AliasTermKind::ProjectionConst { .. } => {
cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into())
let uv = ty::UnevaluatedConst::new(
target_item_def_id.try_into().unwrap(),
target_args,
);
return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv);
}
kind => panic!("expected projection, found {kind:?}"),
};
ecx.instantiate_normalizes_to_term(
goal,
term.instantiate(cx, target_args).skip_norm_wip(),
);
ecx.instantiate_normalizes_to_term(goal, term);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
@@ -701,7 +701,10 @@ pub fn try_evaluate_const<'tcx>(
// logic does not go through type system normalization. If it did this would
// be a backwards compatibility problem as we do not enforce "syntactic" non-
// usage of generic parameters like we do here.
if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
if uv.args.has_non_region_param()
|| uv.args.has_non_region_infer()
|| uv.args.has_non_region_placeholders()
{
return Err(EvaluateConstErr::HasGenericsOrInfers);
}
@@ -1065,7 +1065,7 @@ fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
ty::ConstKind::Unevaluated(uv) => {
if !c.has_escaping_bound_vars() {
// Skip type consts as mGCA doesn't support evaluatable clauses
if !tcx.is_type_const(uv.def) {
if !tcx.is_type_const(uv.def) && !tcx.features().generic_const_args() {
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
ty::ClauseKind::ConstEvaluatable(c),
));
+2
View File
@@ -629,6 +629,8 @@ pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
pub trait Features<I: Interner>: Copy {
fn generic_const_exprs(self) -> bool;
fn generic_const_args(self) -> bool;
fn coroutine_clone(self) -> bool;
fn feature_bound_holds_in_crate(self, symbol: I::Symbol) -> bool;
+1
View File
@@ -203,6 +203,7 @@ fn opt_alias_variances(
fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId)
-> ty::EarlyBinder<Self, Self::Ty>;
fn is_type_const(self, def_id: Self::DefId) -> bool;
fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Const>;
fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind;
+19 -3
View File
@@ -726,16 +726,32 @@ pub fn expect_ty(self, interner: I) -> ty::AliasTy<I> {
AliasTermKind::InherentTy { def_id } => AliasTyKind::Inherent { def_id },
AliasTermKind::OpaqueTy { def_id } => AliasTyKind::Opaque { def_id },
AliasTermKind::FreeTy { def_id } => AliasTyKind::Free { def_id },
AliasTermKind::InherentConst { .. }
kind @ (AliasTermKind::InherentConst { .. }
| AliasTermKind::FreeConst { .. }
| AliasTermKind::UnevaluatedConst { .. }
| AliasTermKind::ProjectionConst { .. } => {
panic!("Cannot turn `UnevaluatedConst` into `AliasTy`")
| AliasTermKind::ProjectionConst { .. }) => {
panic!("Cannot turn `{}` into `AliasTy`", kind.descr())
}
};
ty::AliasTy { kind, args: self.args, _use_alias_ty_new_instead: () }
}
pub fn expect_ct(self, interner: I) -> ty::UnevaluatedConst<I> {
let def_id = match self.kind(interner) {
AliasTermKind::InherentConst { def_id }
| AliasTermKind::FreeConst { def_id }
| AliasTermKind::UnevaluatedConst { def_id }
| AliasTermKind::ProjectionConst { def_id } => def_id,
kind @ (AliasTermKind::ProjectionTy { .. }
| AliasTermKind::InherentTy { .. }
| AliasTermKind::OpaqueTy { .. }
| AliasTermKind::FreeTy { .. }) => {
panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr())
}
};
ty::UnevaluatedConst { def: def_id.try_into().unwrap(), args: self.args }
}
// FIXME: remove this function (access the field instead)
pub fn kind(self, _interner: I) -> AliasTermKind<I> {
self.kind
@@ -69,6 +69,8 @@ in `check_crate` and its AST visitor.
(declared in `rustc_feature::INCOMPATIBLE_FEATURES`) are not used together.
- `check_new_solver_banned_features`: Bans features incompatible with
compiler mode for the next trait solver.
- `check_features_requiring_new_solver`: Requires the new trait solver for
features incompatible with the old solver.
- **Parser-gated spans**: Processes the `GatedSpans` recorded during parsing
(see [Checking `GatedSpans`](#checking-gatedspans)).
@@ -23,7 +23,8 @@ See also: [generic_const_items]
## Examples
```rust
<!-- NOTE(ignore) generic_const_args requires -Znext-solver to compile -->
```rust,ignore (requires-Z-next-solver)
#![feature(generic_const_items)]
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
@@ -0,0 +1,48 @@
error[E0284]: type annotations needed for `([(); _], [(); 10])`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:29:9
|
LL | let (mut arr, mut arr_with_weird_len) = free();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------ type must be known at this point
|
note: required by a const generic parameter in `free`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:24:9
|
LL | fn free<const N: usize>() -> ([(); N], [(); FREE::<N>]) {
| ^^^^^^^^^^^^^^ required by this const generic parameter in `free`
help: consider giving this pattern a type, where the value of const parameter `N` is specified
|
LL | let (mut arr, mut arr_with_weird_len): ([_; N], _) = free();
| +++++++++++++
error[E0271]: type mismatch resolving `10 == 2`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:35:45
|
LL | let (mut arr, mut arr_with_weird_len) = free();
| ^^^^^^ types differ
error[E0284]: type annotations needed for `([(); _], [(); 10])`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:46:9
|
LL | let (mut arr, mut arr_with_weird_len) = proj();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------ type must be known at this point
|
note: required by a const generic parameter in `proj`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:41:9
|
LL | fn proj<const N: usize>() -> ([(); N], [(); <S as Trait>::PROJ::<N>]) {
| ^^^^^^^^^^^^^^ required by this const generic parameter in `proj`
help: consider giving this pattern a type, where the value of const parameter `N` is specified
|
LL | let (mut arr, mut arr_with_weird_len): ([_; N], _) = proj();
| +++++++++++++
error[E0271]: type mismatch resolving `10 == 2`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:52:45
|
LL | let (mut arr, mut arr_with_weird_len) = proj();
| ^^^^^^ types differ
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0271, E0284.
For more information about an error, try `rustc --explain E0271`.
@@ -0,0 +1,10 @@
error: `generic_const_args` requires -Znext-solver=globally to be enabled
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:9:12
|
LL | #![feature(generic_const_args)]
| ^^^^^^^^^^^^^^^^^^
|
= help: enable all of these features
error: aborting due to 1 previous error
@@ -0,0 +1,61 @@
//@ revisions: old next
//@[next] compile-flags: -Znext-solver
// (`test_free_mismatch` is quite difficult to implement in the old solver, so make sure this test
// runs on the old solver, just in case someone attempts to implement GCA for the old solver and
// removes the restriction that -Znext-solver must be enabled)
#![feature(generic_const_items)]
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
//[old]~^ ERROR next-solver
#![expect(incomplete_features)]
const FREE<const A: usize>: usize = 10;
trait Trait {
const PROJ<const A: usize>: usize;
}
struct S;
impl Trait for S {
const PROJ<const A: usize>: usize = 10;
}
fn free<const N: usize>() -> ([(); N], [(); FREE::<N>]) {
loop {}
}
fn test_free() {
let (mut arr, mut arr_with_weird_len) = free();
//[next]~^ ERROR type annotations needed
arr_with_weird_len = [(); 10];
}
fn test_free_mismatch() {
let (mut arr, mut arr_with_weird_len) = free();
//[next]~^ ERROR type mismatch resolving `10 == 2`
arr_with_weird_len = [(); 2];
arr = [(); 10];
}
fn proj<const N: usize>() -> ([(); N], [(); <S as Trait>::PROJ::<N>]) {
loop {}
}
fn test_proj() {
let (mut arr, mut arr_with_weird_len) = proj();
//[next]~^ ERROR type annotations needed
arr_with_weird_len = [(); 10];
}
fn test_proj_mismatch() {
let (mut arr, mut arr_with_weird_len) = proj();
//[next]~^ ERROR type mismatch resolving `10 == 2`
arr_with_weird_len = [(); 2];
arr = [(); 10];
}
fn main() {
test_free();
test_proj();
}
@@ -0,0 +1,43 @@
//@ check-pass
//@ compile-flags: -Znext-solver
#![feature(generic_const_items)]
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
#![expect(incomplete_features)]
const FREE<const A: usize>: usize = 10;
trait Trait {
const PROJ<const A: usize>: usize;
}
struct S;
impl Trait for S {
const PROJ<const A: usize>: usize = 10;
}
fn free<const N: usize>() -> ([(); N], [(); FREE::<N>]) {
loop {}
}
fn test_free() {
let (mut arr, mut arr_with_weird_len) = free();
arr_with_weird_len = [(); 10];
arr = [(); 10];
}
fn proj<const N: usize>() -> ([(); N], [(); <S as Trait>::PROJ::<N>]) {
loop {}
}
fn test_proj() {
let (mut arr, mut arr_with_weird_len) = proj();
arr_with_weird_len = [(); 10];
arr = [(); 10];
}
fn main() {
test_free();
test_proj();
}
@@ -1,4 +1,5 @@
//@ check-pass
//@ compile-flags: -Znext-solver
#![feature(generic_const_items)]
#![feature(min_generic_const_args)]
+1
View File
@@ -1,4 +1,5 @@
//@ check-pass
//@ compile-flags: -Znext-solver
#![feature(generic_const_items)]
#![feature(min_generic_const_args)]
@@ -1,3 +1,4 @@
//@ compile-flags: -Znext-solver
#![feature(generic_const_items, min_generic_const_args, generic_const_args)]
#![expect(incomplete_features)]
@@ -1,5 +1,5 @@
error[E0119]: conflicting implementations of trait `Trait1` for type `[(); 2]`
--> $DIR/coherence-fail.rs:9:1
--> $DIR/coherence-fail.rs:10:1
|
LL | impl Trait1 for [(); FOO::<1>] {}
| ------------------------------ first implementation here
@@ -7,7 +7,7 @@ LL | impl Trait1 for [(); BAR::<1>] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[(); 2]`
error[E0119]: conflicting implementations of trait `Trait2` for type `[(); 1]`
--> $DIR/coherence-fail.rs:16:1
--> $DIR/coherence-fail.rs:17:1
|
LL | impl Trait2 for [(); DIV2::<2>] {}
| ------------------------------- first implementation here
@@ -15,7 +15,7 @@ LL | impl Trait2 for [(); DIV2::<3>] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[(); 1]`
error[E0119]: conflicting implementations of trait `Trait3` for type `[(); 2]`
--> $DIR/coherence-fail.rs:25:1
--> $DIR/coherence-fail.rs:26:1
|
LL | impl Trait3 for [(); ADD1::<1>] {}
| ------------------------------- first implementation here
@@ -1,4 +1,5 @@
//@ check-pass
//@ compile-flags: -Znext-solver
#![feature(generic_const_items, min_generic_const_args, generic_const_args)]
#![expect(incomplete_features)]
@@ -0,0 +1,19 @@
//@ check-pass
//@ compile-flags: -Znext-solver
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
#![feature(generic_const_items)]
#![expect(incomplete_features)]
const ADD1<const N: usize>: usize = N + 1;
fn a<const N: usize>() -> [usize; ADD1::<N>] {
[ADD1::<N>; ADD1::<N>]
}
fn main() {
let _: [(); ADD1::<1>] = [(); ADD1::<1>];
let _: [(); ADD1::<{ ADD1::<1> }>] = [(); ADD1::<2>];
a::<2>();
}
@@ -1,3 +1,4 @@
//@ compile-flags: -Znext-solver
#![feature(min_generic_const_args, generic_const_args)]
#![expect(incomplete_features)]
@@ -1,5 +1,5 @@
error: generic parameters in const blocks are only allowed as the direct value of a `type const`
--> $DIR/generic-param-rhs.rs:6:19
--> $DIR/generic-param-rhs.rs:7:19
|
LL | foo::<const { N + 1 }>();
| ^
@@ -0,0 +1,44 @@
//@ compile-flags: -Znext-solver
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
#![expect(incomplete_features)]
trait Trait {
const PROJECTED_A: usize;
const PROJECTED_B: usize;
}
struct StructImpl;
struct GenericStructImpl<const N: usize>;
impl Trait for StructImpl {
const PROJECTED_A: usize = 1;
const PROJECTED_B: usize = 1;
}
impl<const N: usize> Trait for GenericStructImpl<N> {
const PROJECTED_A: usize = N;
const PROJECTED_B: usize = N;
}
const FREE_A: usize = 1;
const FREE_B: usize = 1;
struct Struct<const N: usize>;
fn f<const N: usize>() {
let _: Struct<{ <GenericStructImpl<N> as Trait>::PROJECTED_A }> =
Struct::<{ <GenericStructImpl<N> as Trait>::PROJECTED_B }>;
//~^ ERROR mismatched types
}
fn g<T: Trait>() {
let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_B }>;
//~^ ERROR mismatched types
}
fn main() {
f::<2>();
g::<StructImpl>();
}
@@ -0,0 +1,25 @@
error[E0308]: mismatched types
--> $DIR/non-type-equality-fail.rs:32:9
|
LL | let _: Struct<{ <GenericStructImpl<N> as Trait>::PROJECTED_A }> =
| -------------------------------------------------------- expected due to this
LL | Struct::<{ <GenericStructImpl<N> as Trait>::PROJECTED_B }>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
|
= note: expected struct `Struct<<GenericStructImpl<N> as Trait>::PROJECTED_A>`
found struct `Struct<<GenericStructImpl<N> as Trait>::PROJECTED_B>`
error[E0308]: mismatched types
--> $DIR/non-type-equality-fail.rs:37:41
|
LL | let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_B }>;
| -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
| |
| expected due to this
|
= note: expected struct `Struct<<T as Trait>::PROJECTED_A>`
found struct `Struct<<T as Trait>::PROJECTED_B>`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.
@@ -0,0 +1,47 @@
//@ check-pass
//@ compile-flags: -Znext-solver
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
#![feature(generic_const_items)]
#![expect(incomplete_features)]
trait Trait {
const PROJECTED_A: usize;
const PROJECTED_B: usize;
}
struct StructImpl;
struct GenericStructImpl<const N: usize>;
impl Trait for StructImpl {
const PROJECTED_A: usize = 1;
const PROJECTED_B: usize = 1;
}
impl<const N: usize> Trait for GenericStructImpl<N> {
const PROJECTED_A: usize = N;
const PROJECTED_B: usize = N;
}
const FREE_A: usize = 1;
const FREE_B: usize = 1;
struct Struct<const N: usize>;
fn f<const N: usize>() {
let _: Struct<{ <GenericStructImpl<N> as Trait>::PROJECTED_A }> =
Struct::<{ <GenericStructImpl<N> as Trait>::PROJECTED_A }>;
}
fn g<T: Trait>() {
let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_A }>;
}
fn main() {
let _: Struct<FREE_A> = Struct::<FREE_B>;
let _: Struct<{ <StructImpl as Trait>::PROJECTED_A }> =
Struct::<{ <StructImpl as Trait>::PROJECTED_B }>;
let _: Struct<{ <GenericStructImpl<2> as Trait>::PROJECTED_A }> =
Struct::<{ <GenericStructImpl<2> as Trait>::PROJECTED_B }>;
}
@@ -0,0 +1,35 @@
//@ check-pass
//@ compile-flags: -Znext-solver
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
#![expect(incomplete_features)]
trait Trait {
const PROJECTED: usize;
}
struct StructImpl;
struct GenericStructImpl<const A: usize>;
const FREE: usize = 1;
impl Trait for StructImpl {
const PROJECTED: usize = 1;
}
impl<const A: usize> Trait for GenericStructImpl<A> {
const PROJECTED: usize = A;
}
struct Struct<const N: usize>;
fn f<T: Trait>() {
let _ = Struct::<{ T::PROJECTED }>;
}
fn main() {
let _ = Struct::<FREE>;
let _ = Struct::<{ <StructImpl as Trait>::PROJECTED }>;
let _ = Struct::<{ <GenericStructImpl<2> as Trait>::PROJECTED }>;
}
@@ -0,0 +1,28 @@
//! This test should be part of path-to-non-type-const.rs, and should pass. However, we are holding
//! off on implementing paths to IACs until a refactoring of how IAC generics are represented.
//@ compile-flags: -Znext-solver
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
#![feature(inherent_associated_types)]
#![expect(incomplete_features)]
struct StructImpl;
struct GenericStructImpl<const A: usize>;
impl StructImpl {
const INHERENT: usize = 1;
}
impl<const A: usize> GenericStructImpl<A> {
const INHERENT: usize = A;
}
struct Struct<const N: usize>;
fn main() {
let _ = Struct::<{ StructImpl::INHERENT }>;
//~^ ERROR use of `const` in the type system not defined as `type const`
let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>;
//~^ ERROR use of `const` in the type system not defined as `type const`
}
@@ -0,0 +1,24 @@
error: use of `const` in the type system not defined as `type const`
--> $DIR/path-to-non-type-inherent-associated-const.rs:24:24
|
LL | let _ = Struct::<{ StructImpl::INHERENT }>;
| ^^^^^^^^^^^^^^^^^^^^
|
help: add `type` before `const` for `StructImpl::INHERENT`
|
LL | type const INHERENT: usize = 1;
| ++++
error: use of `const` in the type system not defined as `type const`
--> $DIR/path-to-non-type-inherent-associated-const.rs:26:24
|
LL | let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add `type` before `const` for `GenericStructImpl::<A>::INHERENT`
|
LL | type const INHERENT: usize = A;
| ++++
error: aborting due to 2 previous errors
@@ -0,0 +1,4 @@
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
//~^ ERROR next-solver
fn main() {}
@@ -0,0 +1,10 @@
error: `generic_const_args` requires -Znext-solver=globally to be enabled
--> $DIR/require-next-solver.rs:2:12
|
LL | #![feature(generic_const_args)]
| ^^^^^^^^^^^^^^^^^^
|
= help: enable all of these features
error: aborting due to 1 previous error
@@ -1,3 +1,4 @@
//@ compile-flags: -Znext-solver
#![feature(generic_const_items)]
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
@@ -1,5 +1,5 @@
error: generic parameters in const blocks are only allowed as the direct value of a `type const`
--> $DIR/rhs-but-not-root.rs:7:54
--> $DIR/rhs-but-not-root.rs:8:54
|
LL | type const FOO<const N: usize>: usize = ID::<const { N + 1 }>;
| ^