diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 5b5986a349f..97091c39c65 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -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`. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 28fe2e062e5..694f3f67bab 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -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!(), } @@ -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() @@ -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::::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) } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 7707cc6de54..6f0d8ca774f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -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}; @@ -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, diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 9d7a9fefd08..99bd296d0d5 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -522,6 +522,17 @@ impl<'cx, 'tcx> TypeFolder> 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)) => { diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 8ca2e403043..41787ee2942 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -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 }; diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index ddc8e7e50eb..377c414456d 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -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> { + TypeError::ConstMismatch(ExpectedFound::new(a_is_expected, a.as_const(tcx), b.as_const(tcx))) +} diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 689945d644c..0596ce373cf 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -156,6 +156,21 @@ impl<'a, 'tcx> TypeFolder> 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!( diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs index cf674d5dda6..dd7f8d35441 100644 --- a/compiler/rustc_infer/src/infer/generalize.rs +++ b/compiler/rustc_infer/src/infer/generalize.rs @@ -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 }) => { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index aaabf1482e2..b31046c2d32 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -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 rustc_span::{sym, 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, + /// Map from effect variable to the effect param it represents. + effect_unification_storage: ut::UnificationTableStorage>, + /// 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> 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> { + 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>, @@ -1159,6 +1182,10 @@ impl<'tcx> InferCtxt<'tcx> { Ty::new_var(self.tcx, ty_var_id).into() } GenericParamDefKind::Const { .. } => { + // todo what about using effect var here + if self.tcx.has_attr(param.def_id, sym::rustc_host) { + return self.var_for_effect(param); + } let origin = ConstVariableOrigin { kind: ConstVariableOriginKind::ConstParameterDefinition( param.name, @@ -1184,6 +1211,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 +1407,10 @@ impl<'tcx> InferCtxt<'tcx> { } } + pub fn probe_effect_var(&self, vid: EffectVid<'tcx>) -> Option> { + 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 +1691,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 +1770,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 +1802,7 @@ impl<'tcx> TyOrConstInferVar<'tcx> { fn maybe_from_const(ct: ty::Const<'tcx>) -> Option { 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 +1846,24 @@ impl<'a, 'tcx> TypeFolder> 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, } } } diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs index 25d06b21ec8..79144b3e643 100644 --- a/compiler/rustc_infer/src/infer/undo_log.rs +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -24,6 +24,7 @@ pub(crate) enum UndoLog<'tcx> { ConstUnificationTable(sv::UndoLog>>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), + EffectUnificationTable(sv::UndoLog>>), RegionConstraintCollector(region_constraints::UndoLog<'tcx>), RegionUnificationTable(sv::UndoLog>>), ProjectionCache(traits::UndoLog<'tcx>), @@ -55,6 +56,7 @@ impl_from! { IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), + EffectUnificationTable(sv::UndoLog>>), ConstUnificationTable(sv::UndoLog>>), @@ -71,6 +73,7 @@ impl<'tcx> Rollback> 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) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 4f4351633a2..7eae164ad3d 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -873,14 +873,14 @@ fn should_encode_attrs(def_kind: DefKind) -> bool { | DefKind::AssocConst | DefKind::Macro(_) | DefKind::Field - | DefKind::Impl { .. } => true, + | DefKind::Impl { .. } + | DefKind::ConstParam => true, // Tools may want to be able to detect their tool lints on // closures from upstream crates, too. This is used by // https://github.com/model-checking/kani and is not a performance // or maintenance issue for us. DefKind::Closure => true, DefKind::TyParam - | DefKind::ConstParam | DefKind::Ctor(..) | DefKind::ExternCrate | DefKind::Use diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 81823118ab8..af9ca6b60c7 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -173,6 +173,7 @@ impl<'tcx> CanonicalVarInfo<'tcx> { CanonicalVarKind::PlaceholderRegion(..) => false, CanonicalVarKind::Const(..) => true, CanonicalVarKind::PlaceholderConst(_, _) => false, + CanonicalVarKind::Effect => true, } } @@ -182,7 +183,8 @@ impl<'tcx> CanonicalVarInfo<'tcx> { CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) | CanonicalVarKind::Const(_, _) - | CanonicalVarKind::PlaceholderConst(_, _) => false, + | CanonicalVarKind::PlaceholderConst(_, _) + | CanonicalVarKind::Effect => false, } } @@ -190,7 +192,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(), @@ -222,6 +225,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>), } @@ -229,11 +235,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, @@ -248,15 +254,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(ui)) - } - CanonicalTyVarKind::Int | CanonicalTyVarKind::Float => { - assert_eq!(ui, ty::UniverseIndex::ROOT); - CanonicalVarKind::Ty(kind) - } - }, + CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => { + CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) + } + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) + | CanonicalVarKind::Effect => { + assert_eq!(ui, ty::UniverseIndex::ROOT); + self + } CanonicalVarKind::PlaceholderTy(placeholder) => { CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: ui, ..placeholder }) } @@ -443,6 +448,14 @@ impl<'tcx> CanonicalVarValues<'tcx> { }; ty::Region::new_late_bound(tcx, ty::INNERMOST, br).into() } + // todo eh? + 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, diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 85fb9214d9d..7ca9647590a 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -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 { + 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>; + #[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" + } +} diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index db4a15fbee5..17fbbd7c6b7 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -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 HashStable 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), } } diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index bbd4a623330..3cafd9a8da9 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -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, _) => { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 1fdc4f7500f..99b697d0ea5 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -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, }; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index f979ddd00fa..e2e1d520548 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -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> 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!() } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 0291cdd6c57..b8627338d4c 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -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)] diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs index a9d182abfb7..77c0f9f090c 100644 --- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs @@ -365,6 +365,9 @@ impl<'tcx> TypeFolder> 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:?}") } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/fallback.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/fallback.rs new file mode 100644 index 00000000000..855bbb2e992 --- /dev/null +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/fallback.rs @@ -0,0 +1,9 @@ +// check-pass + +#![feature(const_trait_impl, effects)] + +pub const fn owo() {} + +fn main() { + let _ = owo; +}