Skip to content

Commit 25a8b5d

Browse files
committed
Fix Instance::resolve() incorrectly returning specialized instances
We only want to return specializations when `Reveal::All` is passed, not when `Reveal::UserFacing` is. Resolving this fixes several issues with the `ConstProp`, `SimplifyBranches`, and `Inline` MIR optimization passes. Fixes #66901
1 parent 41501a6 commit 25a8b5d

File tree

10 files changed

+182
-17
lines changed

10 files changed

+182
-17
lines changed

src/librustc/mir/interpret/queries.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impl<'tcx> TyCtxt<'tcx> {
1818
let substs = InternalSubsts::identity_for_item(self, def_id);
1919
let instance = ty::Instance::new(def_id, substs);
2020
let cid = GlobalId { instance, promoted: None };
21-
let param_env = self.param_env(def_id);
21+
let param_env = self.param_env(def_id).with_reveal_all();
2222
self.const_eval_validated(param_env.and(cid))
2323
}
2424

src/librustc/traits/project.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
10281028
// In either case, we handle this by not adding a
10291029
// candidate for an impl if it contains a `default`
10301030
// type.
1031+
//
1032+
// NOTE: This should be kept in sync with the similar code in
1033+
// `rustc::ty::instance::resolve_associated_item()`.
10311034
let node_item =
10321035
assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id);
10331036

src/librustc/ty/instance.rs

+19
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,25 @@ fn resolve_associated_item<'tcx>(
346346
traits::VtableImpl(impl_data) => {
347347
let (def_id, substs) =
348348
traits::find_associated_item(tcx, param_env, trait_item, rcvr_substs, &impl_data);
349+
350+
let resolved_item = tcx.associated_item(def_id);
351+
352+
// Since this is a trait item, we need to see if the item is either a trait default item
353+
// or a specialization because we can't resolve those unless we can `Reveal::All`.
354+
// NOTE: This should be kept in sync with the similar code in
355+
// `rustc::traits::project::assemble_candidates_from_impls()`.
356+
let eligible = if !resolved_item.defaultness.is_default() {
357+
true
358+
} else if param_env.reveal == traits::Reveal::All {
359+
!trait_ref.needs_subst()
360+
} else {
361+
false
362+
};
363+
364+
if !eligible {
365+
return None;
366+
}
367+
349368
let substs = tcx.erase_regions(&substs);
350369
Some(ty::Instance::new(def_id, substs))
351370
}

src/librustc_mir/const_eval/eval_queries.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,7 @@ pub fn const_eval_validated_provider<'tcx>(
212212
key.param_env.reveal = Reveal::UserFacing;
213213
match tcx.const_eval_validated(key) {
214214
// try again with reveal all as requested
215-
Err(ErrorHandled::TooGeneric) => {
216-
// Promoteds should never be "too generic" when getting evaluated.
217-
// They either don't get evaluated, or we are in a monomorphic context
218-
assert!(key.value.promoted.is_none());
219-
}
215+
Err(ErrorHandled::TooGeneric) => {}
220216
// dedupliate calls
221217
other => return other,
222218
}
@@ -301,10 +297,18 @@ pub fn const_eval_raw_provider<'tcx>(
301297
// Ensure that if the above error was either `TooGeneric` or `Reported`
302298
// an error must be reported.
303299
let v = err.report_as_error(ecx.tcx, "could not evaluate static initializer");
304-
tcx.sess.delay_span_bug(
305-
err.span,
306-
&format!("static eval failure did not emit an error: {:#?}", v),
307-
);
300+
301+
// If this is `Reveal:All`, then we need to make sure an error is reported but if
302+
// this is `Reveal::UserFacing`, then it's expected that we could get a
303+
// `TooGeneric` error. When we fall back to `Reveal::All`, then it will either
304+
// succeed or we'll report this error then.
305+
if key.param_env.reveal == Reveal::All {
306+
tcx.sess.delay_span_bug(
307+
err.span,
308+
&format!("static eval failure did not emit an error: {:#?}", v),
309+
);
310+
}
311+
308312
v
309313
} else if def_id.is_local() {
310314
// constant defined in this crate, we can figure out a lint level!

src/librustc_mir/hair/pattern/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
742742
let kind = match res {
743743
Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => {
744744
let substs = self.tables.node_substs(id);
745-
match self.tcx.const_eval_resolve(self.param_env, def_id, substs, Some(span)) {
745+
// Use `Reveal::All` here because patterns are always monomorphic even if their function isn't.
746+
match self.tcx.const_eval_resolve(
747+
self.param_env.with_reveal_all(),
748+
def_id,
749+
substs,
750+
Some(span),
751+
) {
746752
Ok(value) => {
747753
let pattern = self.const_to_pat(value, id, span);
748754
if !is_associated_const {

src/librustc_mir/transform/const_prop.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::interpret::{
3333
ScalarMaybeUndef, StackPopCleanup,
3434
};
3535
use crate::rustc::ty::subst::Subst;
36+
use crate::rustc::ty::TypeFoldable;
3637
use crate::transform::{MirPass, MirSource};
3738

3839
/// The maximum number of bytes that we'll allocate space for a return value.
@@ -293,13 +294,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
293294
source: MirSource<'tcx>,
294295
) -> ConstPropagator<'mir, 'tcx> {
295296
let def_id = source.def_id();
296-
let param_env = tcx.param_env(def_id);
297+
let substs = &InternalSubsts::identity_for_item(tcx, def_id);
298+
let mut param_env = tcx.param_env(def_id);
299+
300+
// If we're evaluating inside a monomorphic function, then use `Reveal::All` because
301+
// we want to see the same instances that codegen will see. This allows us to `resolve()`
302+
// specializations.
303+
if !substs.needs_subst() {
304+
param_env = param_env.with_reveal_all();
305+
}
306+
297307
let span = tcx.def_span(def_id);
298308
let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine, ());
299309
let can_const_prop = CanConstProp::check(body);
300310

301-
let substs = &InternalSubsts::identity_for_item(tcx, def_id);
302-
303311
let ret = ecx
304312
.layout_of(body.return_ty().subst(tcx, substs))
305313
.ok()

src/librustc_mir/transform/inline.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use rustc_index::vec::{Idx, IndexVec};
88

99
use rustc::mir::visit::*;
1010
use rustc::mir::*;
11-
use rustc::ty::subst::{Subst, SubstsRef};
12-
use rustc::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
11+
use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef};
12+
use rustc::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable};
1313

1414
use super::simplify::{remove_dead_blocks, CfgSimplifier};
1515
use crate::transform::{MirPass, MirSource};
@@ -66,7 +66,14 @@ impl Inliner<'tcx> {
6666

6767
let mut callsites = VecDeque::new();
6868

69-
let param_env = self.tcx.param_env(self.source.def_id());
69+
let mut param_env = self.tcx.param_env(self.source.def_id());
70+
71+
let substs = &InternalSubsts::identity_for_item(self.tcx, self.source.def_id());
72+
73+
// For monomorphic functions, we can use `Reveal::All` to resolve specialized instances.
74+
if !substs.needs_subst() {
75+
param_env = param_env.with_reveal_all();
76+
}
7077

7178
// Only do inlining into fn bodies.
7279
let id = self.tcx.hir().as_local_hir_id(self.source.def_id()).unwrap();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#![feature(specialization)]
2+
3+
fn main() {
4+
let x = <Vec::<()> as Foo>::bar();
5+
}
6+
7+
trait Foo {
8+
fn bar() -> u32;
9+
}
10+
11+
impl<T> Foo for Vec<T> {
12+
#[inline(always)]
13+
default fn bar() -> u32 { 123 }
14+
}
15+
16+
// END RUST SOURCE
17+
// START rustc.main.Inline.before.mir
18+
// let mut _0: ();
19+
// let _1: u32;
20+
// scope 1 {
21+
// debug x => _1;
22+
// }
23+
// bb0: {
24+
// StorageLive(_1);
25+
// _1 = const <std::vec::Vec<()> as Foo>::bar() -> bb1;
26+
// }
27+
// bb1: {
28+
// _0 = ();
29+
// StorageDead(_1);
30+
// return;
31+
// }
32+
// END rustc.main.Inline.before.mir
33+
// START rustc.main.Inline.after.mir
34+
// let mut _0: ();
35+
// let _1: u32;
36+
// scope 1 {
37+
// debug x => _1;
38+
// }
39+
// scope 2 {
40+
// }
41+
// bb0: {
42+
// StorageLive(_1);
43+
// _1 = const 123u32;
44+
// _0 = ();
45+
// StorageDead(_1);
46+
// return;
47+
// }
48+
// END rustc.main.Inline.after.mir
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// ignore-wasm32-bare which doesn't support `std::process:exit()`
2+
// compile-flags: -Zmir-opt-level=2
3+
// run-pass
4+
5+
// Tests that specialization does not cause optimizations running on polymorphic MIR to resolve
6+
// to a `default` implementation.
7+
8+
#![feature(specialization)]
9+
10+
trait Marker {}
11+
12+
trait SpecializedTrait {
13+
const CONST_BOOL: bool;
14+
const CONST_STR: &'static str;
15+
fn method() -> &'static str;
16+
}
17+
impl <T> SpecializedTrait for T {
18+
default const CONST_BOOL: bool = false;
19+
default const CONST_STR: &'static str = "in default impl";
20+
#[inline(always)]
21+
default fn method() -> &'static str {
22+
"in default impl"
23+
}
24+
}
25+
impl <T: Marker> SpecializedTrait for T {
26+
const CONST_BOOL: bool = true;
27+
const CONST_STR: &'static str = "in specialized impl";
28+
fn method() -> &'static str {
29+
"in specialized impl"
30+
}
31+
}
32+
33+
fn const_bool<T>() -> &'static str {
34+
if <T as SpecializedTrait>::CONST_BOOL {
35+
"in specialized impl"
36+
} else {
37+
"in default impl"
38+
}
39+
}
40+
fn const_str<T>() -> &'static str {
41+
<T as SpecializedTrait>::CONST_STR
42+
}
43+
fn run_method<T>() -> &'static str {
44+
<T as SpecializedTrait>::method()
45+
}
46+
47+
struct TypeA;
48+
impl Marker for TypeA {}
49+
struct TypeB;
50+
51+
#[inline(never)]
52+
fn exit_if_not_eq(left: &str, right: &str) {
53+
if left != right {
54+
std::process::exit(1);
55+
}
56+
}
57+
58+
pub fn main() {
59+
exit_if_not_eq("in specialized impl", const_bool::<TypeA>());
60+
exit_if_not_eq("in default impl", const_bool::<TypeB>());
61+
exit_if_not_eq("in specialized impl", const_str::<TypeA>());
62+
exit_if_not_eq("in default impl", const_str::<TypeB>());
63+
exit_if_not_eq("in specialized impl", run_method::<TypeA>());
64+
exit_if_not_eq("in default impl", run_method::<TypeB>());
65+
}

src/test/ui/type-alias-enum-variants/self-in-enum-definition.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ error[E0391]: cycle detected when const-evaluating + checking `Alpha::V3::{{cons
44
LL | V3 = Self::V1 {} as u8 + 2,
55
| ^^^^^^^^
66
|
7+
note: ...which requires const-evaluating + checking `Alpha::V3::{{constant}}#0`...
8+
--> $DIR/self-in-enum-definition.rs:5:10
9+
|
10+
LL | V3 = Self::V1 {} as u8 + 2,
11+
| ^^^^^^^^
712
note: ...which requires const-evaluating `Alpha::V3::{{constant}}#0`...
813
--> $DIR/self-in-enum-definition.rs:5:10
914
|

0 commit comments

Comments
 (0)