Skip to content

Commit d12f030

Browse files
committed
Auto merge of #70452 - eddyb:repeat-expr-correct-generics-parent, r=nikomatsakis
typeck: always expose repeat count `AnonConst`s' parent in `generics_of`. This should reduce some of the confusion around #43408, although, if you look at the changed test outputs (for the last commit), they all hit #68436, so nothing new will start compiling. We can let counts of "repeat expressions" (`N` in `[x; N]`) always have the correct generics parenting, because they're always in a body, so nothing in the `where` clauses or `impl` trait/type of the parent can use it, and therefore no query cycles can occur. <hr/> Other potential candidates we might want to apply the same approach to, are: * ~~(easy) `enum` discriminants (see also #70453)~~ opened #70825 * (trickier) array *type* (not *expression*) lengths nested in: * bodies * types of (associated or not) `const`/`static` * RHS of `type` aliases and associated `type`s * `fn` signatures We should've done so from the start, the only reason we haven't is because I was squeamish about blacklisting some of the cases, but if we whitelist instead we should be fine. Also, lazy normalization is taking forever 😞. <hr/> There's also 5 other commits here: * "typeck: track any errors injected during writeback and taint tables appropriately." - fixes #66706, as the next commit would otherwise trigger an ICE again * "typeck: workaround WF hole in `to_const`." - its purpose is to emulate most of #70107's direct effect, at least in the case of repeat expressions, where the count always goes through `to_const` * this is the reason no new code can really compile, as the WF checks require #68436 to bypass * however, this has more test changes than I hoped, so it should be reviewed separately, and maybe even landed separately (as #70107 might take a while, as it's blocked on a few of my PRs) * "ty: erase lifetimes early in `ty::Const::eval`." - first attempt at fixing #70773 * still useful, I believe the new approach is less likely to cause issues long-term * I could take this out or move it into another PR if desired or someone else could take over (cc @Skinny121) * "traits/query/normalize: add some `debug!` logging for the result." - debugging aid for #70773 * "borrow_check/type_check: normalize `Aggregate` and `Call` operands." - actually fixes #70773 r? @nikomatsakis cc @pnkfelix @varkor @yodaldevoid @oli-obk @estebank
2 parents edc0258 + 8bb7b7b commit d12f030

30 files changed

+276
-118
lines changed

src/librustc_middle/ty/sty.rs

+28-30
Original file line numberDiff line numberDiff line change
@@ -2339,43 +2339,41 @@ impl<'tcx> Const<'tcx> {
23392339
/// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
23402340
/// unevaluated constant.
23412341
pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> {
2342-
let try_const_eval = |did, param_env: ParamEnv<'tcx>, substs, promoted| {
2342+
if let ConstKind::Unevaluated(did, substs, promoted) = self.val {
23432343
let param_env_and_substs = param_env.with_reveal_all().and(substs);
23442344

2345-
// Avoid querying `tcx.const_eval(...)` with any inference vars.
2346-
if param_env_and_substs.needs_infer() {
2347-
return None;
2348-
}
2345+
// HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
2346+
// also does later, but we want to do it before checking for
2347+
// inference variables.
2348+
let param_env_and_substs = tcx.erase_regions(&param_env_and_substs);
2349+
2350+
// HACK(eddyb) when the query key would contain inference variables,
2351+
// attempt using identity substs and `ParamEnv` instead, that will succeed
2352+
// when the expression doesn't depend on any parameters.
2353+
// FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
2354+
// we can call `infcx.const_eval_resolve` which handles inference variables.
2355+
let param_env_and_substs = if param_env_and_substs.needs_infer() {
2356+
tcx.param_env(did).and(InternalSubsts::identity_for_item(tcx, did))
2357+
} else {
2358+
param_env_and_substs
2359+
};
23492360

2361+
// FIXME(eddyb) maybe the `const_eval_*` methods should take
2362+
// `ty::ParamEnvAnd<SubstsRef>` instead of having them separate.
23502363
let (param_env, substs) = param_env_and_substs.into_parts();
2351-
23522364
// try to resolve e.g. associated constants to their definition on an impl, and then
23532365
// evaluate the const.
2354-
tcx.const_eval_resolve(param_env, did, substs, promoted, None)
2355-
.ok()
2356-
.map(|val| Const::from_value(tcx, val, self.ty))
2357-
};
2358-
2359-
match self.val {
2360-
ConstKind::Unevaluated(did, substs, promoted) => {
2361-
// HACK(eddyb) when substs contain inference variables,
2362-
// attempt using identity substs instead, that will succeed
2363-
// when the expression doesn't depend on any parameters.
2364-
// FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
2365-
// we can call `infcx.const_eval_resolve` which handles inference variables.
2366-
if substs.needs_infer() {
2367-
let identity_substs = InternalSubsts::identity_for_item(tcx, did);
2368-
// The `ParamEnv` needs to match the `identity_substs`.
2369-
let identity_param_env = tcx.param_env(did);
2370-
match try_const_eval(did, identity_param_env, identity_substs, promoted) {
2371-
Some(ct) => ct.subst(tcx, substs),
2372-
None => self,
2373-
}
2374-
} else {
2375-
try_const_eval(did, param_env, substs, promoted).unwrap_or(self)
2376-
}
2366+
match tcx.const_eval_resolve(param_env, did, substs, promoted, None) {
2367+
// NOTE(eddyb) `val` contains no lifetimes/types/consts,
2368+
// and we use the original type, so nothing from `substs`
2369+
// (which may be identity substs, see above),
2370+
// can leak through `val` into the const we return.
2371+
Ok(val) => Const::from_value(tcx, val, self.ty),
2372+
2373+
Err(_) => self,
23772374
}
2378-
_ => self,
2375+
} else {
2376+
self
23792377
}
23802378
}
23812379

src/librustc_mir/borrow_check/type_check/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1760,6 +1760,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
17601760
}
17611761
for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() {
17621762
let op_arg_ty = op_arg.ty(body, self.tcx());
1763+
let op_arg_ty = self.normalize(op_arg_ty, term_location);
17631764
let category = if from_hir_call {
17641765
ConstraintCategory::CallArgument
17651766
} else {
@@ -2402,6 +2403,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
24022403
}
24032404
};
24042405
let operand_ty = operand.ty(body, tcx);
2406+
let operand_ty = self.normalize(operand_ty, location);
24052407

24062408
if let Err(terr) = self.sub_types(
24072409
operand_ty,

src/librustc_trait_selection/traits/query/normalize.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,22 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
5959
anon_depth: 0,
6060
};
6161

62-
let value1 = value.fold_with(&mut normalizer);
62+
let result = value.fold_with(&mut normalizer);
63+
debug!(
64+
"normalize::<{}>: result={:?} with {} obligations",
65+
::std::any::type_name::<T>(),
66+
result,
67+
normalizer.obligations.len(),
68+
);
69+
debug!(
70+
"normalize::<{}>: obligations={:?}",
71+
::std::any::type_name::<T>(),
72+
normalizer.obligations,
73+
);
6374
if normalizer.error {
6475
Err(NoSolution)
6576
} else {
66-
Ok(Normalized { value: value1, obligations: normalizer.obligations })
77+
Ok(Normalized { value: result, obligations: normalizer.obligations })
6778
}
6879
}
6980
}

src/librustc_typeck/check/mod.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -3311,8 +3311,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33113311
}
33123312

33133313
pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
3314-
let c = self.tcx.hir().local_def_id(ast_c.hir_id).expect_local();
3315-
ty::Const::from_anon_const(self.tcx, c)
3314+
let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id).expect_local();
3315+
let c = ty::Const::from_anon_const(self.tcx, const_def_id);
3316+
3317+
// HACK(eddyb) emulate what a `WellFormedConst` obligation would do.
3318+
// This code should be replaced with the proper WF handling ASAP.
3319+
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val {
3320+
assert!(promoted.is_none());
3321+
3322+
// HACK(eddyb) let's hope these are always empty.
3323+
// let obligations = self.nominal_obligations(def_id, substs);
3324+
// self.out.extend(obligations);
3325+
3326+
let cause = traits::ObligationCause::new(
3327+
self.tcx.def_span(const_def_id.to_def_id()),
3328+
self.body_id,
3329+
traits::MiscObligation,
3330+
);
3331+
self.register_predicate(traits::Obligation::new(
3332+
cause,
3333+
self.param_env,
3334+
ty::Predicate::ConstEvaluatable(def_id, substs),
3335+
));
3336+
}
3337+
3338+
c
33163339
}
33173340

33183341
// If the type given by the user has free regions, save it for later, since

src/librustc_typeck/check/writeback.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7575
wbcx.tables.upvar_list =
7676
mem::replace(&mut self.tables.borrow_mut().upvar_list, Default::default());
7777

78-
wbcx.tables.tainted_by_errors = self.is_tainted_by_errors();
78+
wbcx.tables.tainted_by_errors |= self.is_tainted_by_errors();
7979

8080
debug!("writeback: tables for {:?} are {:#?}", item_def_id, wbcx.tables);
8181

@@ -578,14 +578,21 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
578578
}
579579
}
580580

581-
fn resolve<T>(&self, x: &T, span: &dyn Locatable) -> T
581+
fn resolve<T>(&mut self, x: &T, span: &dyn Locatable) -> T
582582
where
583583
T: TypeFoldable<'tcx>,
584584
{
585-
let x = x.fold_with(&mut Resolver::new(self.fcx, span, self.body));
585+
let mut resolver = Resolver::new(self.fcx, span, self.body);
586+
let x = x.fold_with(&mut resolver);
586587
if cfg!(debug_assertions) && x.needs_infer() {
587588
span_bug!(span.to_span(self.fcx.tcx), "writeback: `{:?}` has inference variables", x);
588589
}
590+
591+
// We may have introduced e.g. `ty::Error`, if inference failed, make sure
592+
// to mark the `TypeckTables` as tainted in that case, so that downstream
593+
// users of the tables don't produce extra errors, or worse, ICEs.
594+
self.tables.tainted_by_errors |= resolver.replaced_with_error;
595+
589596
x
590597
}
591598
}
@@ -613,6 +620,9 @@ struct Resolver<'cx, 'tcx> {
613620
infcx: &'cx InferCtxt<'cx, 'tcx>,
614621
span: &'cx dyn Locatable,
615622
body: &'tcx hir::Body<'tcx>,
623+
624+
/// Set to `true` if any `Ty` or `ty::Const` had to be replaced with an `Error`.
625+
replaced_with_error: bool,
616626
}
617627

618628
impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
@@ -621,7 +631,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
621631
span: &'cx dyn Locatable,
622632
body: &'tcx hir::Body<'tcx>,
623633
) -> Resolver<'cx, 'tcx> {
624-
Resolver { tcx: fcx.tcx, infcx: fcx, span, body }
634+
Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: false }
625635
}
626636

627637
fn report_error(&self, t: Ty<'tcx>) {
@@ -644,6 +654,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
644654
Err(_) => {
645655
debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
646656
self.report_error(t);
657+
self.replaced_with_error = true;
647658
self.tcx().types.err
648659
}
649660
}
@@ -661,6 +672,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
661672
debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct);
662673
// FIXME: we'd like to use `self.report_error`, but it doesn't yet
663674
// accept a &'tcx ty::Const.
675+
self.replaced_with_error = true;
664676
self.tcx().consts.err
665677
}
666678
}

src/librustc_typeck/collect.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -1170,14 +1170,28 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics {
11701170
}
11711171
// FIXME(#43408) enable this always when we get lazy normalization.
11721172
Node::AnonConst(_) => {
1173+
let parent_id = tcx.hir().get_parent_item(hir_id);
1174+
let parent_def_id = tcx.hir().local_def_id(parent_id);
1175+
11731176
// HACK(eddyb) this provides the correct generics when
11741177
// `feature(const_generics)` is enabled, so that const expressions
11751178
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
11761179
if tcx.features().const_generics {
1177-
let parent_id = tcx.hir().get_parent_item(hir_id);
1178-
Some(tcx.hir().local_def_id(parent_id))
1180+
Some(parent_def_id)
11791181
} else {
1180-
None
1182+
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
1183+
match parent_node {
1184+
// HACK(eddyb) this provides the correct generics for repeat
1185+
// expressions' count (i.e. `N` in `[x; N]`), as they shouldn't
1186+
// be able to cause query cycle errors.
1187+
Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
1188+
if constant.hir_id == hir_id =>
1189+
{
1190+
Some(parent_def_id)
1191+
}
1192+
1193+
_ => None,
1194+
}
11811195
}
11821196
}
11831197
Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => {

src/test/compile-fail/issue-52443.rs

+5
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,9 @@ fn main() {
88
//~| WARN denote infinite loops with
99
[(); { for _ in 0usize.. {}; 0}];
1010
//~^ ERROR `for` is not allowed in a `const`
11+
//~| ERROR calls in constants are limited to constant functions
12+
//~| ERROR references in constants may only refer to immutable values
13+
//~| ERROR calls in constants are limited to constant functions
14+
//~| ERROR constant contains unimplemented expression type
15+
//~| ERROR evaluation of constant value failed
1116
}

src/test/ui/associated-const/associated-const-type-parameter-arrays-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ impl Foo for Def {
1414

1515
pub fn test<A: Foo, B: Foo>() {
1616
let _array = [4; <A as Foo>::Y];
17-
//~^ ERROR the trait bound `A: Foo` is not satisfied [E0277]
17+
//~^ ERROR constant expression depends on a generic parameter
1818
}
1919

2020
fn main() {
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
1-
error[E0277]: the trait bound `A: Foo` is not satisfied
1+
error: constant expression depends on a generic parameter
22
--> $DIR/associated-const-type-parameter-arrays-2.rs:16:22
33
|
4-
LL | const Y: usize;
5-
| --------------- required by `Foo::Y`
6-
...
74
LL | let _array = [4; <A as Foo>::Y];
8-
| ^^^^^^^^^^^^^ the trait `Foo` is not implemented for `A`
5+
| ^^^^^^^^^^^^^
96
|
10-
help: consider further restricting this bound
11-
|
12-
LL | pub fn test<A: Foo + Foo, B: Foo>() {
13-
| ^^^^^
7+
= note: this may fail depending on what value the parameter takes
148

159
error: aborting due to previous error
1610

17-
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
#![feature(const_generics)]
22
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
33

4-
// build-pass
5-
64
fn foo<const N: usize>() {
75
let _ = [0u64; N + 1];
6+
//~^ ERROR constant expression depends on a generic parameter
87
}
98

109
fn main() {}

src/test/ui/const-generics/issues/issue-62456.stderr

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,13 @@ LL | #![feature(const_generics)]
66
|
77
= note: `#[warn(incomplete_features)]` on by default
88

9-
warning: 1 warning emitted
9+
error: constant expression depends on a generic parameter
10+
--> $DIR/issue-62456.rs:5:20
11+
|
12+
LL | let _ = [0u64; N + 1];
13+
| ^^^^^
14+
|
15+
= note: this may fail depending on what value the parameter takes
16+
17+
error: aborting due to previous error; 1 warning emitted
1018

src/test/ui/const-generics/issues/issue-62504.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ impl<const X: usize> ArrayHolder<X> {
1717
pub const fn new() -> Self {
1818
ArrayHolder([0; Self::SIZE])
1919
//~^ ERROR: mismatched types
20+
//~| ERROR constant expression depends on a generic parameter
2021
}
2122
}
2223

src/test/ui/const-generics/issues/issue-62504.stderr

+9-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ LL | ArrayHolder([0; Self::SIZE])
77
= note: expected array `[u32; _]`
88
found array `[u32; _]`
99

10-
error: aborting due to previous error
10+
error: constant expression depends on a generic parameter
11+
--> $DIR/issue-62504.rs:18:25
12+
|
13+
LL | ArrayHolder([0; Self::SIZE])
14+
| ^^^^^^^^^^
15+
|
16+
= note: this may fail depending on what value the parameter takes
17+
18+
error: aborting due to 2 previous errors
1119

1220
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
// check-pass
2-
31
#![allow(incomplete_features, dead_code, unconditional_recursion)]
42
#![feature(const_generics)]
53

64
fn fact<const N: usize>() {
75
fact::<{ N - 1 }>();
6+
//~^ ERROR constant expression depends on a generic parameter
87
}
98

109
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: constant expression depends on a generic parameter
2+
--> $DIR/issue-66205.rs:5:12
3+
|
4+
LL | fact::<{ N - 1 }>();
5+
| ^^^^^^^^^
6+
|
7+
= note: this may fail depending on what value the parameter takes
8+
9+
error: aborting due to previous error
10+

src/test/ui/const-generics/issues/issue-67739.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// Regression test for #67739
22

3-
// check-pass
4-
53
#![allow(incomplete_features)]
64
#![feature(const_generics)]
75

@@ -12,6 +10,7 @@ pub trait Trait {
1210

1311
fn associated_size(&self) -> usize {
1412
[0u8; mem::size_of::<Self::Associated>()];
13+
//~^ ERROR constant expression depends on a generic parameter
1514
0
1615
}
1716
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: constant expression depends on a generic parameter
2+
--> $DIR/issue-67739.rs:12:15
3+
|
4+
LL | [0u8; mem::size_of::<Self::Associated>()];
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this may fail depending on what value the parameter takes
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
fn main() {
22
[(); { &loop { break } as *const _ as usize } ];
33
//~^ ERROR `loop` is not allowed in a `const`
4+
//~| ERROR casting pointers to integers in constants is unstable
5+
//~| ERROR evaluation of constant value failed
46
}

0 commit comments

Comments
 (0)