Skip to content

Commit

Permalink
Implement fallback for effect param
Browse files Browse the repository at this point in the history
  • Loading branch information
fee1-dead committed Sep 10, 2023
1 parent b14b074 commit 84a4907
Show file tree
Hide file tree
Showing 20 changed files with 390 additions and 72 deletions.
64 changes: 51 additions & 13 deletions compiler/rustc_hir_typeck/src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rustc_data_structures::{
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
unord::{UnordBag, UnordMap, UnordSet},
};
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::ty::{self, Ty};

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

// Check if we have any unsolved variables. If not, no need for fallback.
let unsolved_variables = self.unsolved_variables();
if unsolved_variables.is_empty() {
return;
}
let fallback_occured = self.fallback_types() || self.fallback_effects();

let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);

// We do fallback in two passes, to try to generate
// better error messages.
// The first time, we do *not* replace opaque types.
for ty in unsolved_variables {
debug!("unsolved_variable = {:?}", ty);
self.fallback_if_possible(ty, &diverging_fallback);
if !fallback_occured {
return;
}

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

fn fallback_types(&self) -> bool {
// Check if we have any unsolved variables. If not, no need for fallback.
let unsolved_variables = self.unsolved_variables();

if unsolved_variables.is_empty() {
return false;
}

let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);

// We do fallback in two passes, to try to generate
// better error messages.
// The first time, we do *not* replace opaque types.
for ty in unsolved_variables {
debug!("unsolved_variable = {:?}", ty);
self.fallback_if_possible(ty, &diverging_fallback);
}

true
}

fn fallback_effects(&self) -> bool {
let unsolved_effects = self.unsolved_effects();

if unsolved_effects.is_empty() {
return false;
}

// not setting `fallback_has_occured` here because that field is only used for type fallback
// diagnostics.

for effect in unsolved_effects {
let expected = self.tcx.consts.true_;
let cause = self.misc(rustc_span::DUMMY_SP);
match self.at(&cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, effect) {
Ok(InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
}
Err(e) => {
bug!("cannot eq unsolved effect: {e:?}")
}
}
}

true
}

// Tries to apply a fallback to `ty` if it is an unsolved variable.
//
// - Unconstrained ints are replaced with `i32`.
Expand Down
56 changes: 36 additions & 20 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1295,17 +1295,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
self.fcx.ty_infer(Some(param), inf.span).into()
}
(GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
(&GenericParamDefKind::Const { has_default }, GenericArg::Infer(inf)) => {
let tcx = self.fcx.tcx();
self.fcx
.ct_infer(
tcx.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic"),
Some(param),
inf.span,
)
.into()

if has_default && tcx.has_attr(param.def_id, sym::rustc_host) {
self.fcx.var_for_effect(param)
} else {
self.fcx
.ct_infer(
tcx.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic"),
Some(param),
inf.span,
)
.into()
}
}
_ => unreachable!(),
}
Expand All @@ -1324,7 +1329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
GenericParamDefKind::Type { has_default, .. } => {
if !infer_args && has_default {
// If we have a default, then we it doesn't matter that we're not
// If we have a default, then it doesn't matter that we're not
// inferring the type arguments: we provide the default where any
// is missing.
tcx.type_of(param.def_id).instantiate(tcx, args.unwrap()).into()
Expand All @@ -1337,16 +1342,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
GenericParamDefKind::Const { has_default } => {
if !infer_args
&& has_default
&& !tcx.has_attr(param.def_id, sym::rustc_host)
{
tcx.const_param_default(param.def_id)
.instantiate(tcx, args.unwrap())
.into()
} else {
self.fcx.var_for_def(self.span, param)
if has_default {
// N.B. this is a bit of a hack. `infer_args` is passed depending on
// whether the user has provided generic args. E.g. for `Vec::new`
// we would have to infer the generic types. However, for `Vec::<T>::new`
// where the allocator param `A` has a default we will *not* infer. But
// for effect params this is a different story: if the user has not written
// anything explicit for the effect param, we always need to try to infer
// it before falling back to default, such that a `const fn` such as
// `needs_drop::<()>` can still be called in const contexts. (if we defaulted
// instead of inferred, typeck would error)
if tcx.has_attr(param.def_id, sym::rustc_host) {
return self.fcx.var_for_effect(param);
} else if !infer_args {
return tcx
.const_param_default(param.def_id)
.instantiate(tcx, args.unwrap())
.into();
}
}

self.fcx.var_for_def(self.span, param)
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::{self, Span, DUMMY_SP};
use rustc_span::{self, sym, Span, DUMMY_SP};
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};

use std::cell::{Cell, RefCell};
Expand Down Expand Up @@ -266,7 +266,11 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
param: Option<&ty::GenericParamDef>,
span: Span,
) -> Const<'tcx> {
// FIXME ideally this shouldn't use unwrap
match param {
Some(param) if self.tcx.has_attr(param.def_id, sym::rustc_host) => {
self.var_for_effect(param).as_const().unwrap()
}
Some(param) => self.var_for_def(span, param).as_const().unwrap(),
None => self.next_const_var(
ty,
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,17 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
}
}
}
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => {
match self.infcx.probe_effect_var(vid) {
Some(value) => return self.fold_const(value.as_const(self.infcx.tcx)),
None => {
return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::Effect },
ct,
);
}
}
}
ty::ConstKind::Infer(InferConst::Fresh(_)) => {
bug!("encountered a fresh const during canonicalization")
}
Expand Down Expand Up @@ -690,7 +701,8 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
.iter()
.map(|v| CanonicalVarInfo {
kind: match v.kind {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
| CanonicalVarKind::Effect => {
return *v;
}
CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_infer/src/infer/canonical/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ impl<'tcx> InferCtxt<'tcx> {
universe_map(ui),
)
.into(),

CanonicalVarKind::Effect => {
let vid = self.inner.borrow_mut().effect_unification_table().new_key(None);
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(vid), self.tcx.types.bool)
.into()
}
CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }, ty) => {
let universe_mapped = universe_map(universe);
let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound };
Expand Down
69 changes: 64 additions & 5 deletions compiler/rustc_infer/src/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use super::{DefineOpaqueTypes, InferCtxt, TypeTrace};
use crate::infer::generalize::{self, CombineDelegate, Generalization};
use crate::traits::{Obligation, PredicateObligations};
use rustc_middle::infer::canonical::OriginalQueryValues;
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue, EffectVarValue};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::relate::{RelateResult, TypeRelation};
Expand Down Expand Up @@ -91,7 +91,7 @@ impl<'tcx> InferCtxt<'tcx> {
.borrow_mut()
.float_unification_table()
.unify_var_var(a_id, b_id)
.map_err(|e| float_unification_error(relation.a_is_expected(), e))?;
.map_err(|e| float_unification_error(a_is_expected, e))?;
Ok(a)
}
(&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => {
Expand Down Expand Up @@ -210,10 +210,30 @@ impl<'tcx> InferCtxt<'tcx> {
return Ok(a);
}

(
ty::ConstKind::Infer(InferConst::EffectVar(a_vid)),
ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
) => {
self.inner
.borrow_mut()
.effect_unification_table()
.unify_var_var(a_vid, b_vid)
.map_err(|a| effect_unification_error(self.tcx, relation.a_is_expected(), a))?;
return Ok(a);
}

// All other cases of inference with other variables are errors.
(ty::ConstKind::Infer(InferConst::Var(_)), ty::ConstKind::Infer(_))
| (ty::ConstKind::Infer(_), ty::ConstKind::Infer(InferConst::Var(_))) => {
bug!("tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var)")
(
ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
ty::ConstKind::Infer(_),
)
| (
ty::ConstKind::Infer(_),
ty::ConstKind::Infer(InferConst::Var(_) | InferConst::EffectVar(_)),
) => {
bug!(
"tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var): {a:?} and {b:?}"
)
}

(ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
Expand All @@ -223,6 +243,23 @@ impl<'tcx> InferCtxt<'tcx> {
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
return self.unify_const_variable(vid, a, relation.param_env());
}

(ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
return self.unify_effect_variable(
relation.a_is_expected(),
vid,
EffectVarValue::Const(b),
);
}

(_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
return self.unify_effect_variable(
!relation.a_is_expected(),
vid,
EffectVarValue::Const(a),
);
}

(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
{
Expand Down Expand Up @@ -340,6 +377,20 @@ impl<'tcx> InferCtxt<'tcx> {
.map_err(|e| float_unification_error(vid_is_expected, e))?;
Ok(Ty::new_float(self.tcx, val))
}

fn unify_effect_variable(
&self,
vid_is_expected: bool,
vid: ty::EffectVid<'tcx>,
val: EffectVarValue<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.inner
.borrow_mut()
.effect_unification_table()
.unify_var_value(vid, Some(val))
.map_err(|e| effect_unification_error(self.tcx, vid_is_expected, e))?;
Ok(val.as_const(self.tcx))
}
}

impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
Expand Down Expand Up @@ -493,3 +544,11 @@ fn float_unification_error<'tcx>(
let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b))
}

fn effect_unification_error<'tcx>(
tcx: TyCtxt<'tcx>,
a_is_expected: bool,
(a, b): (EffectVarValue<'tcx>, EffectVarValue<'tcx>),
) -> TypeError<'tcx> {
TypeError::ConstMismatch(ExpectedFound::new(a_is_expected, a.as_const(tcx), b.as_const(tcx)))
}
15 changes: 15 additions & 0 deletions compiler/rustc_infer/src/infer/freshen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,21 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
.known();
self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
}
ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
let opt_ct = self
.infcx
.inner
.borrow_mut()
.effect_unification_table()
.probe_value(v)
.map(|effect| effect.as_const(self.infcx.tcx));
self.freshen_const(
opt_ct,
ty::InferConst::EffectVar(v),
ty::InferConst::Fresh,
ct.ty(),
)
}
ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
if i >= self.const_freshen_count {
bug!(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_infer/src/infer/generalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ where
}
}
}
ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c),
// FIXME: remove this branch once `structurally_relate_consts` is fully
// structural.
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {
Expand Down
Loading

0 comments on commit 84a4907

Please sign in to comment.