Skip to content

Commit 84a4907

Browse files
committed
Implement fallback for effect param
1 parent b14b074 commit 84a4907

File tree

20 files changed

+390
-72
lines changed

20 files changed

+390
-72
lines changed

compiler/rustc_hir_typeck/src/fallback.rs

+51-13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_data_structures::{
44
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
55
unord::{UnordBag, UnordMap, UnordSet},
66
};
7+
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
78
use rustc_middle::ty::{self, Ty};
89

910
impl<'tcx> FnCtxt<'_, 'tcx> {
@@ -23,20 +24,10 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
2324
self.fulfillment_cx.borrow_mut().pending_obligations()
2425
);
2526

26-
// Check if we have any unsolved variables. If not, no need for fallback.
27-
let unsolved_variables = self.unsolved_variables();
28-
if unsolved_variables.is_empty() {
29-
return;
30-
}
27+
let fallback_occured = self.fallback_types() || self.fallback_effects();
3128

32-
let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
33-
34-
// We do fallback in two passes, to try to generate
35-
// better error messages.
36-
// The first time, we do *not* replace opaque types.
37-
for ty in unsolved_variables {
38-
debug!("unsolved_variable = {:?}", ty);
39-
self.fallback_if_possible(ty, &diverging_fallback);
29+
if !fallback_occured {
30+
return;
4031
}
4132

4233
// We now see if we can make progress. This might cause us to
@@ -65,6 +56,53 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
6556
self.select_obligations_where_possible(|_| {});
6657
}
6758

59+
fn fallback_types(&self) -> bool {
60+
// Check if we have any unsolved variables. If not, no need for fallback.
61+
let unsolved_variables = self.unsolved_variables();
62+
63+
if unsolved_variables.is_empty() {
64+
return false;
65+
}
66+
67+
let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
68+
69+
// We do fallback in two passes, to try to generate
70+
// better error messages.
71+
// The first time, we do *not* replace opaque types.
72+
for ty in unsolved_variables {
73+
debug!("unsolved_variable = {:?}", ty);
74+
self.fallback_if_possible(ty, &diverging_fallback);
75+
}
76+
77+
true
78+
}
79+
80+
fn fallback_effects(&self) -> bool {
81+
let unsolved_effects = self.unsolved_effects();
82+
83+
if unsolved_effects.is_empty() {
84+
return false;
85+
}
86+
87+
// not setting `fallback_has_occured` here because that field is only used for type fallback
88+
// diagnostics.
89+
90+
for effect in unsolved_effects {
91+
let expected = self.tcx.consts.true_;
92+
let cause = self.misc(rustc_span::DUMMY_SP);
93+
match self.at(&cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, effect) {
94+
Ok(InferOk { obligations, value: () }) => {
95+
self.register_predicates(obligations);
96+
}
97+
Err(e) => {
98+
bug!("cannot eq unsolved effect: {e:?}")
99+
}
100+
}
101+
}
102+
103+
true
104+
}
105+
68106
// Tries to apply a fallback to `ty` if it is an unsolved variable.
69107
//
70108
// - Unconstrained ints are replaced with `i32`.

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+36-20
Original file line numberDiff line numberDiff line change
@@ -1295,17 +1295,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12951295
(GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
12961296
self.fcx.ty_infer(Some(param), inf.span).into()
12971297
}
1298-
(GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
1298+
(&GenericParamDefKind::Const { has_default }, GenericArg::Infer(inf)) => {
12991299
let tcx = self.fcx.tcx();
1300-
self.fcx
1301-
.ct_infer(
1302-
tcx.type_of(param.def_id)
1303-
.no_bound_vars()
1304-
.expect("const parameter types cannot be generic"),
1305-
Some(param),
1306-
inf.span,
1307-
)
1308-
.into()
1300+
1301+
if has_default && tcx.has_attr(param.def_id, sym::rustc_host) {
1302+
self.fcx.var_for_effect(param)
1303+
} else {
1304+
self.fcx
1305+
.ct_infer(
1306+
tcx.type_of(param.def_id)
1307+
.no_bound_vars()
1308+
.expect("const parameter types cannot be generic"),
1309+
Some(param),
1310+
inf.span,
1311+
)
1312+
.into()
1313+
}
13091314
}
13101315
_ => unreachable!(),
13111316
}
@@ -1324,7 +1329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13241329
}
13251330
GenericParamDefKind::Type { has_default, .. } => {
13261331
if !infer_args && has_default {
1327-
// If we have a default, then we it doesn't matter that we're not
1332+
// If we have a default, then it doesn't matter that we're not
13281333
// inferring the type arguments: we provide the default where any
13291334
// is missing.
13301335
tcx.type_of(param.def_id).instantiate(tcx, args.unwrap()).into()
@@ -1337,16 +1342,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13371342
}
13381343
}
13391344
GenericParamDefKind::Const { has_default } => {
1340-
if !infer_args
1341-
&& has_default
1342-
&& !tcx.has_attr(param.def_id, sym::rustc_host)
1343-
{
1344-
tcx.const_param_default(param.def_id)
1345-
.instantiate(tcx, args.unwrap())
1346-
.into()
1347-
} else {
1348-
self.fcx.var_for_def(self.span, param)
1345+
if has_default {
1346+
// N.B. this is a bit of a hack. `infer_args` is passed depending on
1347+
// whether the user has provided generic args. E.g. for `Vec::new`
1348+
// we would have to infer the generic types. However, for `Vec::<T>::new`
1349+
// where the allocator param `A` has a default we will *not* infer. But
1350+
// for effect params this is a different story: if the user has not written
1351+
// anything explicit for the effect param, we always need to try to infer
1352+
// it before falling back to default, such that a `const fn` such as
1353+
// `needs_drop::<()>` can still be called in const contexts. (if we defaulted
1354+
// instead of inferred, typeck would error)
1355+
if tcx.has_attr(param.def_id, sym::rustc_host) {
1356+
return self.fcx.var_for_effect(param);
1357+
} else if !infer_args {
1358+
return tcx
1359+
.const_param_default(param.def_id)
1360+
.instantiate(tcx, args.unwrap())
1361+
.into();
1362+
}
13491363
}
1364+
1365+
self.fcx.var_for_def(self.span, param)
13501366
}
13511367
}
13521368
}

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
2020
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
2121
use rustc_session::Session;
2222
use rustc_span::symbol::Ident;
23-
use rustc_span::{self, Span, DUMMY_SP};
23+
use rustc_span::{self, sym, Span, DUMMY_SP};
2424
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
2525

2626
use std::cell::{Cell, RefCell};
@@ -266,7 +266,11 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
266266
param: Option<&ty::GenericParamDef>,
267267
span: Span,
268268
) -> Const<'tcx> {
269+
// FIXME ideally this shouldn't use unwrap
269270
match param {
271+
Some(param) if self.tcx.has_attr(param.def_id, sym::rustc_host) => {
272+
self.var_for_effect(param).as_const().unwrap()
273+
}
270274
Some(param) => self.var_for_def(span, param).as_const().unwrap(),
271275
None => self.next_const_var(
272276
ty,

compiler/rustc_infer/src/infer/canonical/canonicalizer.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,17 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
522522
}
523523
}
524524
}
525+
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => {
526+
match self.infcx.probe_effect_var(vid) {
527+
Some(value) => return self.fold_const(value.as_const(self.infcx.tcx)),
528+
None => {
529+
return self.canonicalize_const_var(
530+
CanonicalVarInfo { kind: CanonicalVarKind::Effect },
531+
ct,
532+
);
533+
}
534+
}
535+
}
525536
ty::ConstKind::Infer(InferConst::Fresh(_)) => {
526537
bug!("encountered a fresh const during canonicalization")
527538
}
@@ -690,7 +701,8 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
690701
.iter()
691702
.map(|v| CanonicalVarInfo {
692703
kind: match v.kind {
693-
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
704+
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
705+
| CanonicalVarKind::Effect => {
694706
return *v;
695707
}
696708
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {

compiler/rustc_infer/src/infer/canonical/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@ impl<'tcx> InferCtxt<'tcx> {
151151
universe_map(ui),
152152
)
153153
.into(),
154-
154+
CanonicalVarKind::Effect => {
155+
let vid = self.inner.borrow_mut().effect_unification_table().new_key(None);
156+
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid), self.tcx.types.bool)
157+
.into()
158+
}
155159
CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }, ty) => {
156160
let universe_mapped = universe_map(universe);
157161
let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound };

compiler/rustc_infer/src/infer/combine.rs

+64-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use super::{DefineOpaqueTypes, InferCtxt, TypeTrace};
3030
use crate::infer::generalize::{self, CombineDelegate, Generalization};
3131
use crate::traits::{Obligation, PredicateObligations};
3232
use rustc_middle::infer::canonical::OriginalQueryValues;
33-
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
33+
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue};
3434
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
3535
use rustc_middle::ty::error::{ExpectedFound, TypeError};
3636
use rustc_middle::ty::relate::{RelateResult, TypeRelation};
@@ -91,7 +91,7 @@ impl<'tcx> InferCtxt<'tcx> {
9191
.borrow_mut()
9292
.float_unification_table()
9393
.unify_var_var(a_id, b_id)
94-
.map_err(|e| float_unification_error(relation.a_is_expected(), e))?;
94+
.map_err(|e| float_unification_error(a_is_expected, e))?;
9595
Ok(a)
9696
}
9797
(&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => {
@@ -210,10 +210,30 @@ impl<'tcx> InferCtxt<'tcx> {
210210
return Ok(a);
211211
}
212212

213+
(
214+
ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
215+
ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
216+
) => {
217+
self.inner
218+
.borrow_mut()
219+
.effect_unification_table()
220+
.unify_var_var(a_vid, b_vid)
221+
.map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?;
222+
return Ok(a);
223+
}
224+
213225
// All other cases of inference with other variables are errors.
214-
(ty::ConstKind::Infer(InferConst::Var(_)), ty::ConstKind::Infer(_))
215-
| (ty::ConstKind::Infer(_), ty::ConstKind::Infer(InferConst::Var(_))) => {
216-
bug!("tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var)")
226+
(
227+
ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
228+
ty::ConstKind::Infer(_),
229+
)
230+
| (
231+
ty::ConstKind::Infer(_),
232+
ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
233+
) => {
234+
bug!(
235+
"tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
236+
)
217237
}
218238

219239
(ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
@@ -223,6 +243,23 @@ impl<'tcx> InferCtxt<'tcx> {
223243
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
224244
return self.unify_const_variable(vid, a, relation.param_env());
225245
}
246+
247+
(ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
248+
return self.unify_effect_variable(
249+
relation.a_is_expected(),
250+
vid,
251+
EffectVarValue::Const(b),
252+
);
253+
}
254+
255+
(_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
256+
return self.unify_effect_variable(
257+
!relation.a_is_expected(),
258+
vid,
259+
EffectVarValue::Const(a),
260+
);
261+
}
262+
226263
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
227264
if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
228265
{
@@ -340,6 +377,20 @@ impl<'tcx> InferCtxt<'tcx> {
340377
.map_err(|e| float_unification_error(vid_is_expected, e))?;
341378
Ok(Ty::new_float(self.tcx, val))
342379
}
380+
381+
fn unify_effect_variable(
382+
&self,
383+
vid_is_expected: bool,
384+
vid: ty::EffectVid<'tcx>,
385+
val: EffectVarValue<'tcx>,
386+
) -> RelateResult<'tcx, ty::Const<'tcx>> {
387+
self.inner
388+
.borrow_mut()
389+
.effect_unification_table()
390+
.unify_var_value(vid, Some(val))
391+
.map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?;
392+
Ok(val.as_const(self.tcx))
393+
}
343394
}
344395

345396
impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
@@ -493,3 +544,11 @@ fn float_unification_error<'tcx>(
493544
let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
494545
TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
495546
}
547+
548+
fn effect_unification_error<'tcx>(
549+
tcx: TyCtxt<'tcx>,
550+
a_is_expected: bool,
551+
(a, b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>),
552+
) -> TypeError<'tcx> {
553+
TypeError::ConstMismatch(ExpectedFound::new(a_is_expected, a.as_const(tcx), b.as_const(tcx)))
554+
}

compiler/rustc_infer/src/infer/freshen.rs

+15
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,21 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
156156
.known();
157157
self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
158158
}
159+
ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
160+
let opt_ct = self
161+
.infcx
162+
.inner
163+
.borrow_mut()
164+
.effect_unification_table()
165+
.probe_value(v)
166+
.map(|effect| effect.as_const(self.infcx.tcx));
167+
self.freshen_const(
168+
opt_ct,
169+
ty::InferConst::EffectVar(v),
170+
ty::InferConst::Fresh,
171+
ct.ty(),
172+
)
173+
}
159174
ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
160175
if i >= self.const_freshen_count {
161176
bug!(

compiler/rustc_infer/src/infer/generalize.rs

+1
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ where
403403
}
404404
}
405405
}
406+
ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c),
406407
// FIXME: remove this branch once `structurally_relate_consts` is fully
407408
// structural.
408409
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {

0 commit comments

Comments
 (0)