Skip to content

Commit 89bdbd0

Browse files
authored
Rollup merge of #97105 - JulianKnodt:const_dep_gen_const_expr, r=lcnr
Add tests for lint on type dependent on consts r? `@lcnr`
2 parents fbb1777 + ee8efc5 commit 89bdbd0

File tree

6 files changed

+244
-134
lines changed

6 files changed

+244
-134
lines changed

compiler/rustc_trait_selection/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#![feature(label_break_value)]
2121
#![feature(let_chains)]
2222
#![feature(let_else)]
23+
#![feature(if_let_guard)]
2324
#![feature(never_type)]
2425
#![recursion_limit = "512"] // For rustdoc
2526

compiler/rustc_trait_selection/src/traits/const_evaluatable.rs

+132-134
Original file line numberDiff line numberDiff line change
@@ -39,150 +39,148 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
3939
let tcx = infcx.tcx;
4040

4141
if tcx.features().generic_const_exprs {
42-
match AbstractConst::new(tcx, uv)? {
43-
// We are looking at a generic abstract constant.
44-
Some(ct) => {
45-
if satisfied_from_param_env(tcx, ct, param_env)? {
46-
return Ok(());
47-
}
48-
49-
// We were unable to unify the abstract constant with
50-
// a constant found in the caller bounds, there are
51-
// now three possible cases here.
52-
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
53-
enum FailureKind {
54-
/// The abstract const still references an inference
55-
/// variable, in this case we return `TooGeneric`.
56-
MentionsInfer,
57-
/// The abstract const references a generic parameter,
58-
/// this means that we emit an error here.
59-
MentionsParam,
60-
/// The substs are concrete enough that we can simply
61-
/// try and evaluate the given constant.
62-
Concrete,
63-
}
64-
let mut failure_kind = FailureKind::Concrete;
65-
walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) {
66-
Node::Leaf(leaf) => {
67-
if leaf.has_infer_types_or_consts() {
68-
failure_kind = FailureKind::MentionsInfer;
69-
} else if leaf.has_param_types_or_consts() {
70-
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
71-
}
72-
73-
ControlFlow::CONTINUE
42+
if let Some(ct) = AbstractConst::new(tcx, uv)? {
43+
if satisfied_from_param_env(tcx, ct, param_env)? {
44+
return Ok(());
45+
}
46+
47+
// We were unable to unify the abstract constant with
48+
// a constant found in the caller bounds, there are
49+
// now three possible cases here.
50+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
51+
enum FailureKind {
52+
/// The abstract const still references an inference
53+
/// variable, in this case we return `TooGeneric`.
54+
MentionsInfer,
55+
/// The abstract const references a generic parameter,
56+
/// this means that we emit an error here.
57+
MentionsParam,
58+
/// The substs are concrete enough that we can simply
59+
/// try and evaluate the given constant.
60+
Concrete,
61+
}
62+
let mut failure_kind = FailureKind::Concrete;
63+
walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) {
64+
Node::Leaf(leaf) => {
65+
if leaf.has_infer_types_or_consts() {
66+
failure_kind = FailureKind::MentionsInfer;
67+
} else if leaf.has_param_types_or_consts() {
68+
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
7469
}
75-
Node::Cast(_, _, ty) => {
76-
if ty.has_infer_types_or_consts() {
77-
failure_kind = FailureKind::MentionsInfer;
78-
} else if ty.has_param_types_or_consts() {
79-
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
80-
}
8170

82-
ControlFlow::CONTINUE
83-
}
84-
Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {
85-
ControlFlow::CONTINUE
71+
ControlFlow::CONTINUE
72+
}
73+
Node::Cast(_, _, ty) => {
74+
if ty.has_infer_types_or_consts() {
75+
failure_kind = FailureKind::MentionsInfer;
76+
} else if ty.has_param_types_or_consts() {
77+
failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
8678
}
87-
});
8879

89-
match failure_kind {
90-
FailureKind::MentionsInfer => {
91-
return Err(NotConstEvaluatable::MentionsInfer);
92-
}
93-
FailureKind::MentionsParam => {
94-
return Err(NotConstEvaluatable::MentionsParam);
95-
}
96-
FailureKind::Concrete => {
97-
// Dealt with below by the same code which handles this
98-
// without the feature gate.
99-
}
80+
ControlFlow::CONTINUE
10081
}
101-
}
102-
None => {
103-
// If we are dealing with a concrete constant, we can
104-
// reuse the old code path and try to evaluate
105-
// the constant.
106-
}
107-
}
108-
}
82+
Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {
83+
ControlFlow::CONTINUE
84+
}
85+
});
10986

110-
let future_compat_lint = || {
111-
if let Some(local_def_id) = uv.def.did.as_local() {
112-
infcx.tcx.struct_span_lint_hir(
113-
lint::builtin::CONST_EVALUATABLE_UNCHECKED,
114-
infcx.tcx.hir().local_def_id_to_hir_id(local_def_id),
115-
span,
116-
|err| {
117-
err.build("cannot use constants which depend on generic parameters in types")
118-
.emit();
119-
},
120-
);
121-
}
122-
};
123-
124-
// FIXME: We should only try to evaluate a given constant here if it is fully concrete
125-
// as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
126-
//
127-
// We previously did not check this, so we only emit a future compat warning if
128-
// const evaluation succeeds and the given constant is still polymorphic for now
129-
// and hopefully soon change this to an error.
130-
//
131-
// See #74595 for more details about this.
132-
let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
133-
134-
if concrete.is_ok() && uv.substs.has_param_types_or_consts() {
135-
match infcx.tcx.def_kind(uv.def.did) {
136-
DefKind::AnonConst | DefKind::InlineConst => {
137-
let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def);
138-
139-
if mir_body.is_polymorphic {
140-
future_compat_lint();
87+
match failure_kind {
88+
FailureKind::MentionsInfer => {
89+
return Err(NotConstEvaluatable::MentionsInfer);
90+
}
91+
FailureKind::MentionsParam => {
92+
return Err(NotConstEvaluatable::MentionsParam);
14193
}
94+
// returned below
95+
FailureKind::Concrete => {}
14296
}
143-
_ => future_compat_lint(),
14497
}
145-
}
146-
147-
// If we're evaluating a foreign constant, under a nightly compiler without generic
148-
// const exprs, AND it would've passed if that expression had been evaluated with
149-
// generic const exprs, then suggest using generic const exprs.
150-
if concrete.is_err()
151-
&& tcx.sess.is_nightly_build()
152-
&& !uv.def.did.is_local()
153-
&& !tcx.features().generic_const_exprs
154-
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
155-
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true)
156-
{
157-
tcx.sess
158-
.struct_span_fatal(
159-
// Slightly better span than just using `span` alone
160-
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
161-
"failed to evaluate generic const expression",
162-
)
163-
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
164-
.span_suggestion_verbose(
165-
rustc_span::DUMMY_SP,
166-
"consider enabling this feature",
167-
"#![feature(generic_const_exprs)]\n".to_string(),
168-
rustc_errors::Applicability::MaybeIncorrect,
169-
)
170-
.emit()
171-
}
172-
173-
debug!(?concrete, "is_const_evaluatable");
174-
match concrete {
175-
Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
176-
true => NotConstEvaluatable::MentionsInfer,
177-
false => NotConstEvaluatable::MentionsParam,
178-
}),
179-
Err(ErrorHandled::Linted) => {
180-
let reported =
181-
infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint");
182-
Err(NotConstEvaluatable::Error(reported))
98+
let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
99+
match concrete {
100+
Err(ErrorHandled::TooGeneric) => Err(if !uv.has_infer_types_or_consts() {
101+
infcx
102+
.tcx
103+
.sess
104+
.delay_span_bug(span, &format!("unexpected `TooGeneric` for {:?}", uv));
105+
NotConstEvaluatable::MentionsParam
106+
} else {
107+
NotConstEvaluatable::MentionsInfer
108+
}),
109+
Err(ErrorHandled::Linted) => {
110+
let reported = infcx
111+
.tcx
112+
.sess
113+
.delay_span_bug(span, "constant in type had error reported as lint");
114+
Err(NotConstEvaluatable::Error(reported))
115+
}
116+
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
117+
Ok(_) => Ok(()),
118+
}
119+
} else {
120+
// FIXME: We should only try to evaluate a given constant here if it is fully concrete
121+
// as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
122+
//
123+
// We previously did not check this, so we only emit a future compat warning if
124+
// const evaluation succeeds and the given constant is still polymorphic for now
125+
// and hopefully soon change this to an error.
126+
//
127+
// See #74595 for more details about this.
128+
let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
129+
130+
match concrete {
131+
// If we're evaluating a foreign constant, under a nightly compiler without generic
132+
// const exprs, AND it would've passed if that expression had been evaluated with
133+
// generic const exprs, then suggest using generic const exprs.
134+
Err(_) if tcx.sess.is_nightly_build()
135+
&& let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
136+
&& satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => {
137+
tcx.sess
138+
.struct_span_fatal(
139+
// Slightly better span than just using `span` alone
140+
if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
141+
"failed to evaluate generic const expression",
142+
)
143+
.note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
144+
.span_suggestion_verbose(
145+
rustc_span::DUMMY_SP,
146+
"consider enabling this feature",
147+
"#![feature(generic_const_exprs)]\n".to_string(),
148+
rustc_errors::Applicability::MaybeIncorrect,
149+
)
150+
.emit()
151+
}
152+
153+
Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() {
154+
NotConstEvaluatable::MentionsInfer
155+
} else {
156+
NotConstEvaluatable::MentionsParam
157+
}),
158+
Err(ErrorHandled::Linted) => {
159+
let reported =
160+
infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint");
161+
Err(NotConstEvaluatable::Error(reported))
162+
}
163+
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
164+
Ok(_) => {
165+
if uv.substs.has_param_types_or_consts() {
166+
assert!(matches!(infcx.tcx.def_kind(uv.def.did), DefKind::AnonConst));
167+
let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def);
168+
169+
if mir_body.is_polymorphic {
170+
let Some(local_def_id) = uv.def.did.as_local() else { return Ok(()) };
171+
tcx.struct_span_lint_hir(
172+
lint::builtin::CONST_EVALUATABLE_UNCHECKED,
173+
tcx.hir().local_def_id_to_hir_id(local_def_id),
174+
span,
175+
|err| {
176+
err.build("cannot use constants which depend on generic parameters in types").emit();
177+
})
178+
}
179+
}
180+
181+
Ok(())
182+
},
183183
}
184-
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
185-
Ok(_) => Ok(()),
186184
}
187185
}
188186

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error: generic parameters may not be used in const operations
2+
--> $DIR/dependence_lint.rs:13:32
3+
|
4+
LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce
5+
| ^ cannot perform const operation using `T`
6+
|
7+
= note: type parameters may not be used in const expressions
8+
= help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
9+
10+
error: generic parameters may not be used in const operations
11+
--> $DIR/dependence_lint.rs:20:37
12+
|
13+
LL | let _: [u8; if true { size_of::<T>() } else { 3 }]; // error on stable, error with gce
14+
| ^ cannot perform const operation using `T`
15+
|
16+
= note: type parameters may not be used in const expressions
17+
= help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
18+
19+
warning: cannot use constants which depend on generic parameters in types
20+
--> $DIR/dependence_lint.rs:9:9
21+
|
22+
LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs`
23+
| ^^^^^^^^^^^^^^^^^^^
24+
|
25+
= note: `#[warn(const_evaluatable_unchecked)]` on by default
26+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
27+
= note: for more information, see issue #76200 <https://github.com/rust-lang/rust/issues/76200>
28+
29+
warning: cannot use constants which depend on generic parameters in types
30+
--> $DIR/dependence_lint.rs:16:9
31+
|
32+
LL | [0; if false { size_of::<T>() } else { 3 }]; // lint on stable, error with gce
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
34+
|
35+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
36+
= note: for more information, see issue #76200 <https://github.com/rust-lang/rust/issues/76200>
37+
38+
error: aborting due to 2 previous errors; 2 warnings emitted
39+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error: overly complex generic constant
2+
--> $DIR/dependence_lint.rs:16:9
3+
|
4+
LL | [0; if false { size_of::<T>() } else { 3 }]; // lint on stable, error with gce
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants
6+
|
7+
= help: consider moving this anonymous constant into a `const` function
8+
9+
error: overly complex generic constant
10+
--> $DIR/dependence_lint.rs:20:17
11+
|
12+
LL | let _: [u8; if true { size_of::<T>() } else { 3 }]; // error on stable, error with gce
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants
14+
|
15+
= help: consider moving this anonymous constant into a `const` function
16+
17+
error: unconstrained generic constant
18+
--> $DIR/dependence_lint.rs:13:12
19+
|
20+
LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
22+
|
23+
= help: try adding a `where` bound using this expression: `where [(); size_of::<*mut T>()]:`
24+
25+
error: unconstrained generic constant
26+
--> $DIR/dependence_lint.rs:9:9
27+
|
28+
LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs`
29+
| ^^^^^^^^^^^^^^^^^^^
30+
|
31+
= help: try adding a `where` bound using this expression: `where [(); size_of::<*mut T>()]:`
32+
33+
error: aborting due to 4 previous errors
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// revisions: full gce
2+
3+
#![cfg_attr(gce, feature(generic_const_exprs))]
4+
#![allow(incomplete_features)]
5+
6+
use std::mem::size_of;
7+
8+
fn foo<T>() {
9+
[0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs`
10+
//[gce]~^ ERROR unconstrained
11+
//[full]~^^ WARNING cannot use constants
12+
//[full]~| WARNING this was previously accepted
13+
let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce
14+
//[full]~^ ERROR generic parameters may not be used
15+
//[gce]~^^ ERROR unconstrained generic
16+
[0; if false { size_of::<T>() } else { 3 }]; // lint on stable, error with gce
17+
//[gce]~^ ERROR overly complex
18+
//[full]~^^ WARNING cannot use constants
19+
//[full]~| WARNING this was previously accepted
20+
let _: [u8; if true { size_of::<T>() } else { 3 }]; // error on stable, error with gce
21+
//[full]~^ ERROR generic parameters may not be used
22+
//[gce]~^^ ERROR overly complex
23+
}
24+
25+
fn main() {}

0 commit comments

Comments
 (0)