Rollup merge of #115727 - fee1-dead-contrib:effect-fallback, r=oli-obk

Implement fallback for effect param

r? `@oli-obk` or `@lcnr`

tracking issue for this ongoing work: https://github.com/rust-lang/rust/issues/110395
This commit is contained in:
Matthias Krüger 2023-09-11 17:03:31 +02:00 committed by GitHub
commit e7a347baf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 404 additions and 79 deletions

View File

@ -523,7 +523,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Ty::new_misc_error(tcx).into()
}
}
GenericParamDefKind::Const { has_default } => {
GenericParamDefKind::Const { has_default, .. } => {
let ty = tcx
.at(self.span)
.type_of(param.def_id)

View File

@ -1255,7 +1255,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
let is_our_default = |def: &ty::GenericParamDef| match def.kind {
GenericParamDefKind::Type { has_default, .. }
| GenericParamDefKind::Const { has_default } => {
| GenericParamDefKind::Const { has_default, .. } => {
has_default && def.index >= generics.parent_count as u32
}
GenericParamDefKind::Lifetime => unreachable!(),

View File

@ -328,7 +328,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
name: param.name.ident().name,
def_id: param.def_id.to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
kind: ty::GenericParamDefKind::Const {
has_default: default.is_some(),
is_host_effect: is_host_param,
},
})
}
}));

View File

@ -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> {
@ -23,22 +24,12 @@ 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() {
let fallback_occured = self.fallback_types() || self.fallback_effects();
if !fallback_occured {
return;
}
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);
}
// We now see if we can make progress. This might cause us to
// unify inference variables for opaque types, since we may
// have unified some other type variables during the first
@ -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`.

View File

@ -1295,8 +1295,15 @@ 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, is_host_effect },
GenericArg::Infer(inf),
) => {
let tcx = self.fcx.tcx();
if has_default && is_host_effect {
self.fcx.var_for_effect(param)
} else {
self.fcx
.ct_infer(
tcx.type_of(param.def_id)
@ -1307,6 +1314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
.into()
}
}
_ => unreachable!(),
}
}
@ -1324,7 +1332,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()
@ -1336,18 +1344,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.fcx.var_for_def(self.span, param)
}
}
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)
GenericParamDefKind::Const { has_default, is_host_effect } => {
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 is_host_effect {
return self.fcx.var_for_effect(param);
} else if !infer_args {
return tcx
.const_param_default(param.def_id)
.instantiate(tcx, args.unwrap())
.into()
} else {
self.fcx.var_for_def(self.span, param)
.into();
}
}
self.fcx.var_for_def(self.span, param)
}
}
}
}

View File

@ -266,7 +266,14 @@ 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 @ ty::GenericParamDef {
kind: ty::GenericParamDefKind::Const { is_host_effect: true, .. },
..
},
) => 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,

View File

@ -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")
}
@ -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)) => {

View File

@ -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 };

View File

@ -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};
@ -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)) => {
@ -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)), _) => {
@ -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() =>
{
@ -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> {
@ -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> {
bug!("unexpected effect unification error")
}

View File

@ -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!(

View File

@ -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 }) => {

View File

@ -21,7 +21,7 @@ use rustc_data_structures::unify as ut;
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
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, ToType};
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
use rustc_middle::mir::ConstraintCategory;
@ -33,13 +33,14 @@ use rustc_middle::ty::relate::RelateResult;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
pub use rustc_middle::ty::IntVarValue;
use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtxt};
use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid};
use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
use std::cell::{Cell, RefCell};
use std::fmt;
use std::marker::PhantomData;
use self::combine::CombineFields;
use self::error_reporting::TypeErrCtxt;
@ -115,6 +116,9 @@ pub struct InferCtxtInner<'tcx> {
/// Map from floating variable to the kind of float it represents.
float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>,
/// Map from effect variable to the effect param it represents.
effect_unification_storage: ut::UnificationTableStorage<ty::EffectVid<'tcx>>,
/// Tracks the set of region variables and the constraints between them.
///
/// This is initially `Some(_)` but when
@ -172,6 +176,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
const_unification_storage: ut::UnificationTableStorage::new(),
int_unification_storage: ut::UnificationTableStorage::new(),
float_unification_storage: ut::UnificationTableStorage::new(),
effect_unification_storage: ut::UnificationTableStorage::new(),
region_constraint_storage: Some(RegionConstraintStorage::new()),
region_obligations: vec![],
opaque_type_storage: Default::default(),
@ -223,6 +228,10 @@ impl<'tcx> InferCtxtInner<'tcx> {
self.const_unification_storage.with_log(&mut self.undo_log)
}
fn effect_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::EffectVid<'tcx>> {
self.effect_unification_storage.with_log(&mut self.undo_log)
}
#[inline]
pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> {
self.region_constraint_storage
@ -356,6 +365,7 @@ impl<'tcx> ty::InferCtxtLike<TyCtxt<'tcx>> for InferCtxt<'tcx> {
Err(universe) => Some(universe),
Ok(_) => None,
},
EffectVar(_) => None,
Fresh(_) => None,
}
}
@ -777,6 +787,19 @@ impl<'tcx> InferCtxt<'tcx> {
vars
}
pub fn unsolved_effects(&self) -> Vec<ty::Const<'tcx>> {
let mut inner = self.inner.borrow_mut();
let mut table = inner.effect_unification_table();
(0..table.len())
.map(|i| ty::EffectVid { index: i as u32, phantom: PhantomData })
.filter(|&vid| table.probe_value(vid).is_none())
.map(|v| {
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(v), self.tcx.types.bool)
})
.collect()
}
fn combine_fields<'a>(
&'a self,
trace: TypeTrace<'tcx>,
@ -1158,7 +1181,10 @@ impl<'tcx> InferCtxt<'tcx> {
Ty::new_var(self.tcx, ty_var_id).into()
}
GenericParamDefKind::Const { .. } => {
GenericParamDefKind::Const { is_host_effect, .. } => {
if is_host_effect {
return self.var_for_effect(param);
}
let origin = ConstVariableOrigin {
kind: ConstVariableOriginKind::ConstParameterDefinition(
param.name,
@ -1184,6 +1210,17 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
let effect_vid = self.inner.borrow_mut().effect_unification_table().new_key(None);
let ty = self
.tcx
.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic");
debug_assert_eq!(self.tcx.types.bool, ty);
ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid), ty).into()
}
/// Given a set of generics defined on a type or impl, returns a substitution mapping each
/// type/region parameter to a fresh inference variable.
pub fn fresh_args_for_item(&self, span: Span, def_id: DefId) -> GenericArgsRef<'tcx> {
@ -1369,6 +1406,10 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
pub fn probe_effect_var(&self, vid: EffectVid<'tcx>) -> Option<EffectVarValue<'tcx>> {
self.inner.borrow_mut().effect_unification_table().probe_value(vid)
}
/// Attempts to resolve all type/region/const variables in
/// `value`. Region inference must have been run already (e.g.,
/// by calling `resolve_regions_and_report_errors`). If some
@ -1649,6 +1690,14 @@ impl<'tcx> InferCtxt<'tcx> {
ConstVariableValue::Known { .. } => true,
}
}
TyOrConstInferVar::Effect(v) => {
// If `probe_value` returns `Some`, it never equals
// `ty::ConstKind::Infer(ty::InferConst::Effect(v))`.
//
// Not `inlined_probe_value(v)` because this call site is colder.
self.probe_effect_var(v).is_some()
}
}
}
}
@ -1720,6 +1769,8 @@ pub enum TyOrConstInferVar<'tcx> {
/// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`.
Const(ConstVid<'tcx>),
/// Equivalent to `ty::ConstKind::Infer(ty::InferConst::EffectVar(_))`.
Effect(EffectVid<'tcx>),
}
impl<'tcx> TyOrConstInferVar<'tcx> {
@ -1750,6 +1801,7 @@ impl<'tcx> TyOrConstInferVar<'tcx> {
fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> {
match ct.kind() {
ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)),
ty::ConstKind::Infer(InferConst::EffectVar(v)) => Some(TyOrConstInferVar::Effect(v)),
_ => None,
}
}
@ -1793,17 +1845,24 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ShallowResolver<'a, 'tcx> {
}
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() {
self.infcx
match ct.kind() {
ty::ConstKind::Infer(InferConst::Var(vid)) => self
.infcx
.inner
.borrow_mut()
.const_unification_table()
.probe_value(vid)
.val
.known()
.unwrap_or(ct)
} else {
ct
.unwrap_or(ct),
ty::ConstKind::Infer(InferConst::EffectVar(vid)) => self
.infcx
.inner
.borrow_mut()
.effect_unification_table()
.probe_value(vid)
.map_or(ct, |val| val.as_const(self.infcx.tcx)),
_ => ct,
}
}
}

View File

@ -24,6 +24,7 @@ pub(crate) enum UndoLog<'tcx> {
ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
EffectUnificationTable(sv::UndoLog<ut::Delegate<ty::EffectVid<'tcx>>>),
RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
ProjectionCache(traits::UndoLog<'tcx>),
@ -55,6 +56,7 @@ impl_from! {
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
EffectUnificationTable(sv::UndoLog<ut::Delegate<ty::EffectVid<'tcx>>>),
ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
@ -71,6 +73,7 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
UndoLog::EffectUnificationTable(undo) => self.effect_unification_storage.reverse(undo),
UndoLog::RegionConstraintCollector(undo) => {
self.region_constraint_storage.as_mut().unwrap().reverse(undo)
}

View File

@ -184,6 +184,7 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(..) => true,
CanonicalVarKind::PlaceholderConst(_, _) => false,
CanonicalVarKind::Effect => true,
}
}
@ -193,7 +194,8 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_, _)
| CanonicalVarKind::PlaceholderConst(_, _) => false,
| CanonicalVarKind::PlaceholderConst(_, _)
| CanonicalVarKind::Effect => false,
}
}
@ -201,7 +203,8 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
match self.kind {
CanonicalVarKind::Ty(_)
| CanonicalVarKind::Region(_)
| CanonicalVarKind::Const(_, _) => bug!("expected placeholder: {self:?}"),
| CanonicalVarKind::Const(_, _)
| CanonicalVarKind::Effect => bug!("expected placeholder: {self:?}"),
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.bound.var.as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.bound.var.as_usize(),
@ -233,6 +236,9 @@ pub enum CanonicalVarKind<'tcx> {
/// Some kind of const inference variable.
Const(ty::UniverseIndex, Ty<'tcx>),
/// Effect variable `'?E`.
Effect,
/// A "placeholder" that represents "any const".
PlaceholderConst(ty::PlaceholderConst<'tcx>, Ty<'tcx>),
}
@ -240,11 +246,11 @@ pub enum CanonicalVarKind<'tcx> {
impl<'tcx> CanonicalVarKind<'tcx> {
pub fn universe(self) -> ty::UniverseIndex {
match self {
CanonicalVarKind::Ty(kind) => match kind {
CanonicalTyVarKind::General(ui) => ui,
CanonicalTyVarKind::Float | CanonicalTyVarKind::Int => ty::UniverseIndex::ROOT,
},
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => ui,
CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => {
ty::UniverseIndex::ROOT
}
CanonicalVarKind::Effect => ty::UniverseIndex::ROOT,
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe,
CanonicalVarKind::Region(ui) => ui,
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe,
@ -259,15 +265,14 @@ impl<'tcx> CanonicalVarKind<'tcx> {
/// the updated universe is not the root.
pub fn with_updated_universe(self, ui: ty::UniverseIndex) -> CanonicalVarKind<'tcx> {
match self {
CanonicalVarKind::Ty(kind) => match kind {
CanonicalTyVarKind::General(_) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
}
CanonicalTyVarKind::Int | CanonicalTyVarKind::Float => {
CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
| CanonicalVarKind::Effect => {
assert_eq!(ui, ty::UniverseIndex::ROOT);
CanonicalVarKind::Ty(kind)
self
}
},
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: ui, ..placeholder })
}
@ -454,6 +459,13 @@ impl<'tcx> CanonicalVarValues<'tcx> {
};
ty::Region::new_late_bound(tcx, ty::INNERMOST, br).into()
}
CanonicalVarKind::Effect => ty::Const::new_bound(
tcx,
ty::INNERMOST,
ty::BoundVar::from_usize(i),
tcx.types.bool,
)
.into(),
CanonicalVarKind::Const(_, ty)
| CanonicalVarKind::PlaceholderConst(_, ty) => ty::Const::new_bound(
tcx,

View File

@ -188,3 +188,53 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> {
})
}
}
/// values for the effect inference variable
#[derive(Clone, Copy, Debug)]
pub enum EffectVarValue<'tcx> {
/// The host effect is on, enabling access to syscalls, filesystem access, etc.
Host,
/// The host effect is off. Execution is restricted to const operations only.
NoHost,
Const(ty::Const<'tcx>),
}
impl<'tcx> EffectVarValue<'tcx> {
pub fn as_const(self, tcx: TyCtxt<'tcx>) -> ty::Const<'tcx> {
match self {
EffectVarValue::Host => tcx.consts.true_,
EffectVarValue::NoHost => tcx.consts.false_,
EffectVarValue::Const(c) => c,
}
}
}
impl<'tcx> UnifyValue for EffectVarValue<'tcx> {
type Error = (EffectVarValue<'tcx>, EffectVarValue<'tcx>);
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
match (value1, value2) {
(EffectVarValue::Host, EffectVarValue::Host) => Ok(EffectVarValue::Host),
(EffectVarValue::NoHost, EffectVarValue::NoHost) => Ok(EffectVarValue::NoHost),
(EffectVarValue::NoHost | EffectVarValue::Host, _)
| (_, EffectVarValue::NoHost | EffectVarValue::Host) => Err((*value1, *value2)),
(EffectVarValue::Const(_), EffectVarValue::Const(_)) => {
bug!("equating two const variables, both of which have known values")
}
}
}
}
impl<'tcx> UnifyKey for ty::EffectVid<'tcx> {
type Value = Option<EffectVarValue<'tcx>>;
#[inline]
fn index(&self) -> u32 {
self.index
}
#[inline]
fn from_index(i: u32) -> Self {
ty::EffectVid { index: i, phantom: PhantomData }
}
fn tag() -> &'static str {
"EffectVid"
}
}

View File

@ -55,6 +55,11 @@ static_assert_size!(super::ConstKind<'_>, 32);
pub enum InferConst<'tcx> {
/// Infer the value of the const.
Var(ty::ConstVid<'tcx>),
/// Infer the value of the effect.
///
/// For why this is separate from the `Var` variant above, see the
/// documentation on `EffectVid`.
EffectVar(ty::EffectVid<'tcx>),
/// A fresh const variable. See `infer::freshen` for more details.
Fresh(u32),
}
@ -62,7 +67,9 @@ pub enum InferConst<'tcx> {
impl<CTX> HashStable<CTX> for InferConst<'_> {
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
match self {
InferConst::Var(_) => panic!("const variables should not be hashed: {self:?}"),
InferConst::Var(_) | InferConst::EffectVar(_) => {
panic!("const variables should not be hashed: {self:?}")
}
InferConst::Fresh(i) => i.hash_stable(hcx, hasher),
}
}

View File

@ -324,7 +324,9 @@ impl FlagComputation {
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
match infer {
InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH),
InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER),
InferConst::Var(_) | InferConst::EffectVar(_) => {
self.add_flags(TypeFlags::HAS_CT_INFER)
}
}
}
ty::ConstKind::Bound(debruijn, _) => {

View File

@ -12,7 +12,7 @@ use super::{Clause, EarlyBoundRegion, InstantiatedPredicates, ParamConst, ParamT
pub enum GenericParamDefKind {
Lifetime,
Type { has_default: bool, synthetic: bool },
Const { has_default: bool },
Const { has_default: bool, is_host_effect: bool },
}
impl GenericParamDefKind {
@ -87,7 +87,7 @@ impl GenericParamDef {
GenericParamDefKind::Type { has_default, .. } if has_default => {
Some(tcx.type_of(self.def_id).map_bound(|t| t.into()))
}
GenericParamDefKind::Const { has_default } if has_default => {
GenericParamDefKind::Const { has_default, .. } if has_default => {
Some(tcx.const_param_default(self.def_id).map_bound(|c| c.into()))
}
_ => None,
@ -187,7 +187,7 @@ impl<'tcx> Generics {
GenericParamDefKind::Type { has_default, .. } => {
own_defaults.types += has_default as usize;
}
GenericParamDefKind::Const { has_default } => {
GenericParamDefKind::Const { has_default, .. } => {
own_defaults.consts += has_default as usize;
}
}

View File

@ -98,9 +98,9 @@ pub use self::sty::BoundRegionKind::*;
pub use self::sty::{
AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar,
BoundVariableKind, CanonicalPolyFnSig, ClosureArgs, ClosureArgsParts, ConstKind, ConstVid,
EarlyBoundRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
FreeRegion, GenSig, GeneratorArgs, GeneratorArgsParts, InlineConstArgs, InlineConstArgsParts,
ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection,
EarlyBoundRegion, EffectVid, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef,
FnSig, FreeRegion, GenSig, GeneratorArgs, GeneratorArgsParts, InlineConstArgs,
InlineConstArgsParts, ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection,
PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, Region, RegionKind, RegionVid,
TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo,
};

View File

@ -138,6 +138,12 @@ impl<'tcx> fmt::Debug for ty::ConstVid<'tcx> {
}
}
impl fmt::Debug for ty::EffectVid<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "?{}e", self.index)
}
}
impl<'tcx> fmt::Debug for ty::TraitRef<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with_no_trimmed_paths!(fmt::Display::fmt(self, f))
@ -253,6 +259,7 @@ impl<'tcx> fmt::Debug for ty::InferConst<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InferConst::Var(var) => write!(f, "{var:?}"),
InferConst::EffectVar(var) => write!(f, "{var:?}"),
InferConst::Fresh(var) => write!(f, "Fresh({var:?})"),
}
}
@ -267,6 +274,7 @@ impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::InferConst<'tcx> {
None => write!(f, "{:?}", this.data),
Some(universe) => match *this.data {
Var(vid) => write!(f, "?{}_{}c", vid.index, universe.index()),
EffectVar(vid) => write!(f, "?{}_{}e", vid.index, universe.index()),
Fresh(_) => {
unreachable!()
}

View File

@ -1577,6 +1577,20 @@ pub struct ConstVid<'tcx> {
pub phantom: PhantomData<&'tcx ()>,
}
/// An **effect** **v**ariable **ID**.
///
/// Handling effect infer variables happens separately from const infer variables
/// because we do not want to reuse any of the const infer machinery. If we try to
/// relate an effect variable with a normal one, we would ICE, which can catch bugs
/// where we are not correctly using the effect var for an effect param. Fallback
/// is also implemented on top of having separate effect and normal const variables.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(TyEncodable, TyDecodable)]
pub struct EffectVid<'tcx> {
pub index: u32,
pub phantom: PhantomData<&'tcx ()>,
}
rustc_index::newtype_index! {
/// A **region** (lifetime) **v**ariable **ID**.
#[derive(HashStable)]

View File

@ -836,7 +836,7 @@ impl ReachEverythingInTheInterfaceVisitor<'_, '_> {
self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity());
}
}
GenericParamDefKind::Const { has_default } => {
GenericParamDefKind::Const { has_default, .. } => {
self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity());
if has_default {
self.visit(

View File

@ -1297,7 +1297,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind {
ty::GenericParamDefKind::Type { has_default, synthetic } => {
GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic }
}
ty::GenericParamDefKind::Const { has_default } => {
ty::GenericParamDefKind::Const { has_default, is_host_effect: _ } => {
GenericParamDefKind::Const { has_default: *has_default }
}
}

View File

@ -365,6 +365,9 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
// FIXME: we should fold this ty eventually
CanonicalVarKind::Const(ui, c.ty())
}
ty::ConstKind::Infer(ty::InferConst::EffectVar(_)) => {
bug!("effect var has no universe")
}
ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => {
bug!("fresh var during canonicalization: {c:?}")
}

View File

@ -542,7 +542,7 @@ fn clean_generic_param_def<'tcx>(
},
)
}
ty::GenericParamDefKind::Const { has_default } => (
ty::GenericParamDefKind::Const { has_default, .. } => (
def.name,
GenericParamDefKind::Const {
ty: Box::new(clean_middle_ty(

View File

@ -0,0 +1,9 @@
// check-pass
#![feature(const_trait_impl, effects)]
pub const fn owo() {}
fn main() {
let _ = owo;
}