Skip to content

Commit 7ffb868

Browse files
committed
resolve instances in ctfe based on current const context
1 parent 64a965e commit 7ffb868

10 files changed

Lines changed: 192 additions & 20 deletions

File tree

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
952952

953953
fn get_default_alloc_params(&self) -> <Self::Bytes as mir::interpret::AllocBytes>::AllocParams {
954954
}
955+
956+
const SHOULD_RESPECT_CONST_BOUNDS_WHEN_RESOLVING_INSTANCES: bool = true;
955957
}
956958

957959
// Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
370370
trace!("resolve: {:?}, {:#?}", def, args);
371371
trace!("typing_env: {:#?}", self.typing_env);
372372
trace!("args: {:#?}", args);
373-
match ty::Instance::try_resolve(*self.tcx, self.typing_env, def, args) {
373+
let resolve = if M::SHOULD_RESPECT_CONST_BOUNDS_WHEN_RESOLVING_INSTANCES {
374+
ty::Instance::try_resolve_for_ctfe
375+
} else {
376+
ty::Instance::try_resolve
377+
};
378+
match resolve(*self.tcx, self.typing_env, def, args) {
374379
Ok(Some(instance)) => interp_ok(instance),
375380
Ok(None) => throw_inval!(TooGeneric),
376381

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,8 @@ pub trait Machine<'tcx>: Sized {
638638
fn enter_trace_span(_span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan {
639639
()
640640
}
641+
642+
const SHOULD_RESPECT_CONST_BOUNDS_WHEN_RESOLVING_INSTANCES: bool = false;
641643
}
642644

643645
/// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines

compiler/rustc_hir/src/hir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4535,7 +4535,7 @@ impl fmt::Display for Safety {
45354535
}
45364536
}
45374537

4538-
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, StableHash)]
4538+
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, StableHash, Hash)]
45394539
#[derive(Default)]
45404540
pub enum Constness {
45414541
#[default]

compiler/rustc_middle/src/queries.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2619,7 +2619,7 @@ rustc_queries! {
26192619
/// from `Ok(None)` to avoid misleading diagnostics when an error
26202620
/// has already been/will be emitted, for the original cause.
26212621
query resolve_instance_raw(
2622-
key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>)>
2622+
key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>, hir::Constness)>
26232623
) -> Result<Option<ty::Instance<'tcx>>, ErrorGuaranteed> {
26242624
desc { "resolving instance `{}`", ty::Instance::new_raw(key.value.0, key.value.1) }
26252625
}

compiler/rustc_middle/src/query/keys.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::hash::Hash;
77
use rustc_ast::tokenstream::TokenStream;
88
use rustc_data_structures::sso::SsoHashSet;
99
use rustc_data_structures::stable_hash::StableHash;
10+
use rustc_hir as hir;
1011
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
1112
use rustc_hir::hir_id::OwnerId;
1213
use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol};
@@ -244,6 +245,12 @@ impl<'tcx> QueryKey for (DefId, GenericArgsRef<'tcx>) {
244245
}
245246
}
246247

248+
impl<'tcx> QueryKey for (DefId, GenericArgsRef<'tcx>, hir::Constness) {
249+
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
250+
self.0.default_span(tcx)
251+
}
252+
}
253+
247254
impl<'tcx> QueryKey for ty::TraitRef<'tcx> {
248255
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
249256
tcx.def_span(self.def_id)

compiler/rustc_middle/src/ty/instance.rs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -493,15 +493,16 @@ impl<'tcx> Instance<'tcx> {
493493
/// couldn't complete due to errors elsewhere - this is distinct
494494
/// from `Ok(None)` to avoid misleading diagnostics when an error
495495
/// has already been/will be emitted, for the original cause
496-
#[instrument(level = "debug", skip(tcx), ret)]
497-
pub fn try_resolve(
496+
fn try_resolve_inner(
498497
tcx: TyCtxt<'tcx>,
499498
typing_env: ty::TypingEnv<'tcx>,
500499
def_id: DefId,
501500
args: GenericArgsRef<'tcx>,
501+
mut constness: hir::Constness,
502502
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
503+
let def_kind = tcx.def_kind(def_id);
503504
assert_matches!(
504-
tcx.def_kind(def_id),
505+
def_kind,
505506
DefKind::Fn
506507
| DefKind::AssocFn
507508
| DefKind::Const { .. }
@@ -517,6 +518,10 @@ impl<'tcx> Instance<'tcx> {
517518
`try_normalize_erasing_regions`."
518519
);
519520

521+
if !def_kind.is_assoc() {
522+
constness = hir::Constness::NotConst;
523+
}
524+
520525
// Rust code can easily create exponentially-long types using only a
521526
// polynomial recursion depth. Even with the default recursion
522527
// depth, you can easily get cases that take >2^60 steps to run,
@@ -529,11 +534,36 @@ impl<'tcx> Instance<'tcx> {
529534
return Ok(None);
530535
}
531536

537+
let input = tcx.erase_and_anonymize_regions(typing_env.as_query_input((def_id, args)));
538+
532539
// All regions in the result of this query are erased, so it's
533540
// fine to erase all of the input regions.
534-
tcx.resolve_instance_raw(
535-
tcx.erase_and_anonymize_regions(typing_env.as_query_input((def_id, args))),
536-
)
541+
tcx.resolve_instance_raw(ty::PseudoCanonicalInput {
542+
typing_env: input.typing_env,
543+
value: (input.value.0, input.value.1, constness),
544+
})
545+
}
546+
547+
/// See `try_resolve_inner`.
548+
#[instrument(level = "debug", skip(tcx), ret)]
549+
pub fn try_resolve(
550+
tcx: TyCtxt<'tcx>,
551+
typing_env: ty::TypingEnv<'tcx>,
552+
def_id: DefId,
553+
args: GenericArgsRef<'tcx>,
554+
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
555+
Self::try_resolve_inner(tcx, typing_env, def_id, args, hir::Constness::NotConst)
556+
}
557+
558+
/// See `try_resolve_inner`.
559+
#[instrument(level = "debug", skip(tcx), ret)]
560+
pub fn try_resolve_for_ctfe(
561+
tcx: TyCtxt<'tcx>,
562+
typing_env: ty::TypingEnv<'tcx>,
563+
def_id: DefId,
564+
args: GenericArgsRef<'tcx>,
565+
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
566+
Self::try_resolve_inner(tcx, typing_env, def_id, args, hir::Constness::Const)
537567
}
538568

539569
pub fn expect_resolve(

compiler/rustc_trait_selection/src/solve/select.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_infer::traits::{
88
Selection, SelectionError, SelectionResult, TraitObligation,
99
};
1010
use rustc_macros::extension;
11-
use rustc_middle::{bug, span_bug};
11+
use rustc_middle::{bug, span_bug, ty};
1212
use rustc_span::Span;
1313
use thin_vec::thin_vec;
1414

@@ -30,6 +30,19 @@ impl<'tcx> InferCtxt<'tcx> {
3030
.break_value()
3131
.unwrap()
3232
}
33+
fn select_host_effect_predicate_in_new_trait_solver(
34+
&self,
35+
obligation: &Obligation<'tcx, ty::HostEffectPredicate<'tcx>>,
36+
) -> SelectionResult<'tcx, Selection<'tcx>> {
37+
assert!(self.next_trait_solver());
38+
39+
self.visit_proof_tree(
40+
Goal::new(self.tcx, obligation.param_env, ty::Binder::dummy(obligation.predicate)),
41+
&mut Select { span: obligation.cause.span },
42+
)
43+
.break_value()
44+
.unwrap()
45+
}
3346
}
3447

3548
struct Select {

compiler/rustc_ty_utils/src/instance.rs

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use rustc_errors::ErrorGuaranteed;
2-
use rustc_hir::LangItem;
32
use rustc_hir::def_id::DefId;
3+
use rustc_hir::{Constness, LangItem};
44
use rustc_infer::infer::TyCtxtInferExt;
5+
use rustc_infer::traits::{
6+
ImplSource, Obligation, ObligationCause, ScrubbedTraitError, SelectionError,
7+
};
58
use rustc_middle::bug;
69
use rustc_middle::query::Providers;
710
use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError};
@@ -10,17 +13,19 @@ use rustc_middle::ty::{
1013
Unnormalized,
1114
};
1215
use rustc_span::sym;
13-
use rustc_trait_selection::traits;
16+
use rustc_trait_selection::error_reporting::InferCtxtErrorExt as _;
17+
use rustc_trait_selection::solve::InferCtxtSelectExt as _;
18+
use rustc_trait_selection::traits::{self, ObligationCtxt};
1419
use tracing::debug;
1520
use traits::translate_args;
1621

1722
use crate::errors::UnexpectedFnPtrAssociatedItem;
1823

1924
fn resolve_instance_raw<'tcx>(
2025
tcx: TyCtxt<'tcx>,
21-
key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>)>,
26+
key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>, Constness)>,
2227
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
23-
let PseudoCanonicalInput { typing_env, value: (def_id, args) } = key;
28+
let PseudoCanonicalInput { typing_env, value: (def_id, args, constness) } = key;
2429

2530
let result = if let Some(trait_def_id) = tcx.trait_of_assoc(def_id) {
2631
debug!(" => associated item, attempting to find impl in typing_env {:#?}", typing_env);
@@ -30,6 +35,7 @@ fn resolve_instance_raw<'tcx>(
3035
typing_env,
3136
trait_def_id,
3237
tcx.normalize_erasing_regions(typing_env, Unnormalized::new_wip(args)),
38+
constness,
3339
)
3440
} else {
3541
let def = if tcx.intrinsic(def_id).is_some() {
@@ -101,24 +107,88 @@ fn resolve_instance_raw<'tcx>(
101107
result
102108
}
103109

110+
#[tracing::instrument(level = "debug", skip(tcx))]
104111
fn resolve_associated_item<'tcx>(
105112
tcx: TyCtxt<'tcx>,
106113
trait_item_id: DefId,
107114
typing_env: ty::TypingEnv<'tcx>,
108115
trait_id: DefId,
109116
rcvr_args: GenericArgsRef<'tcx>,
117+
constness: Constness,
110118
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
111-
debug!(?trait_item_id, ?typing_env, ?trait_id, ?rcvr_args, "resolve_associated_item");
112-
113119
let trait_ref = ty::TraitRef::from_assoc(tcx, trait_id, rcvr_args);
114120

115121
let input = typing_env.as_query_input(trait_ref);
116-
let vtbl = match tcx.codegen_select_candidate(input) {
117-
Ok(vtbl) => vtbl,
118-
Err(CodegenObligationError::Ambiguity | CodegenObligationError::Unimplemented) => {
122+
let vtbl = if constness == Constness::Const && tcx.next_trait_solver_globally() {
123+
let (infcx, param_env) =
124+
tcx.infer_ctxt().ignoring_regions().build_with_typing_env(typing_env);
125+
126+
let obligation_cause = ObligationCause::dummy();
127+
let host_predicate =
128+
ty::HostEffectPredicate { trait_ref, constness: ty::BoundConstness::Const };
129+
let obligation = Obligation::new(tcx, obligation_cause, param_env, host_predicate);
130+
131+
let selection = match infcx.select_host_effect_predicate_in_new_trait_solver(&obligation) {
132+
Ok(Some(selection)) => selection,
133+
Ok(None) => return Ok(None),
134+
Err(SelectionError::Unimplemented) => return Ok(None),
135+
Err(e) => {
136+
bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, host_predicate)
137+
}
138+
};
139+
140+
debug!(?selection);
141+
142+
// Currently, we use a fulfillment context to completely resolve
143+
// all nested obligations. This is because they can inform the
144+
// inference of the impl's type parameters.
145+
let ocx = ObligationCtxt::new(&infcx);
146+
let impl_source = selection.map(|obligation| {
147+
ocx.register_obligation(obligation);
148+
});
149+
150+
// In principle, we only need to do this so long as `impl_source`
151+
// contains unbound type parameters. It could be a slight
152+
// optimization to stop iterating early.
153+
let errors = ocx.evaluate_obligations_error_on_ambiguity();
154+
if !errors.is_empty() {
155+
// `rustc_monomorphize::collector` assumes there are no type errors.
156+
// Cycle errors are the only post-monomorphization errors possible; emit them now so
157+
// `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
158+
for err in errors {
159+
if let ScrubbedTraitError::Cycle(cycle) = err {
160+
infcx.err_ctxt().report_overflow_obligation_cycle(&cycle);
161+
}
162+
}
119163
return Ok(None);
120164
}
121-
Err(CodegenObligationError::UnconstrainedParam(guar)) => return Err(guar),
165+
166+
let impl_source = infcx.resolve_vars_if_possible(impl_source);
167+
let impl_source = tcx.erase_and_anonymize_regions(impl_source);
168+
if impl_source.has_non_region_infer() {
169+
// Unused generic types or consts on an impl get replaced with inference vars,
170+
// but never resolved, causing the return value of a query to contain inference
171+
// vars. We do not have a concept for this and will in fact ICE in stable hashing
172+
// of the return value. So bail out instead.
173+
let guar = match impl_source {
174+
ImplSource::UserDefined(impl_) => tcx.dcx().span_delayed_bug(
175+
tcx.def_span(impl_.impl_def_id),
176+
"this impl has unconstrained generic parameters",
177+
),
178+
_ => unreachable!(),
179+
};
180+
return Err(guar);
181+
}
182+
183+
&*tcx.arena.alloc(impl_source)
184+
} else {
185+
match tcx.codegen_select_candidate(input) {
186+
Ok(vtbl) => vtbl,
187+
Err(CodegenObligationError::Ambiguity | CodegenObligationError::Unimplemented) => {
188+
return Ok(None);
189+
}
190+
Err(CodegenObligationError::UnconstrainedParam(guar)) => return Err(guar),
191+
}
122192
};
123193

124194
// Now that we know which impl is being used, we can dispatch to
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//@ compile-flags: -Znext-solver
2+
//@ check-pass
3+
4+
#![feature(min_specialization, const_trait_impl)]
5+
6+
struct Dummy;
7+
8+
const trait DummyTrait {
9+
fn dummy_fn() -> u32;
10+
}
11+
impl DummyTrait for Wrap<i32> {
12+
fn dummy_fn() -> u32 {
13+
println!("wut");
14+
0
15+
}
16+
}
17+
18+
const trait Trait {
19+
fn trait_fn() -> u32;
20+
}
21+
impl<T> const Trait for T where T: DummyTrait {
22+
default fn trait_fn() -> u32 {
23+
42
24+
}
25+
}
26+
27+
struct Wrap<T>(T);
28+
29+
impl<T> const Trait for Wrap<T> where Self: [const] DummyTrait {
30+
fn trait_fn() -> u32 {
31+
<Wrap<T>>::dummy_fn()
32+
}
33+
}
34+
35+
const fn indirect<T: DummyTrait>() -> u32 {
36+
T::trait_fn()
37+
}
38+
39+
const A: u32 = indirect::<Wrap<i32>>();
40+
41+
const B: () = { assert!(A == 42); };
42+
43+
fn main() {}

0 commit comments

Comments
 (0)