diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 7cd55951cdaf4..0e9dbcac5cd64 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -10,10 +10,12 @@ use crate::infer::canonical::{ OriginalQueryValues, }; use crate::infer::InferCtxt; +use crate::mir::interpret::ConstValue; use std::sync::atomic::Ordering; use crate::ty::fold::{TypeFoldable, TypeFolder}; use crate::ty::subst::Kind; -use crate::ty::{self, BoundVar, Lift, List, Ty, TyCtxt, TypeFlags}; +use crate::ty::{self, BoundVar, InferConst, Lift, List, Ty, TyCtxt, TypeFlags}; +use crate::ty::flags::FlagComputation; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; @@ -432,6 +434,61 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> } } } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + match ct.val { + ConstValue::Infer(InferConst::Var(vid)) => { + debug!("canonical: const var found with vid {:?}", vid); + match self.infcx.unwrap().probe_const_var(vid) { + Ok(c) => { + debug!("(resolved to {:?})", c); + return self.fold_const(c); + } + + // `ConstVar(vid)` is unresolved, track its universe index in the + // canonicalized result + Err(mut ui) => { + if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } + return self.canonicalize_const_var( + CanonicalVarInfo { + kind: CanonicalVarKind::Const(ui), + }, + ct, + ); + } + } + } + ConstValue::Infer(InferConst::Fresh(_)) => { + bug!("encountered a fresh const during canonicalization") + } + ConstValue::Infer(InferConst::Canonical(debruijn, _)) => { + if debruijn >= self.binder_index { + bug!("escaping bound type during canonicalization") + } else { + return ct; + } + } + ConstValue::Placeholder(placeholder) => { + return self.canonicalize_const_var( + CanonicalVarInfo { + kind: CanonicalVarKind::PlaceholderConst(placeholder), + }, + ct, + ); + } + _ => {} + } + + let flags = FlagComputation::for_const(ct); + if flags.intersects(self.needs_canonical_flags) { + ct.super_fold_with(self) + } else { + ct + } + } } impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { @@ -450,11 +507,13 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { let needs_canonical_flags = if canonicalize_region_mode.any() { TypeFlags::KEEP_IN_LOCAL_TCX | TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS` - TypeFlags::HAS_TY_PLACEHOLDER + TypeFlags::HAS_TY_PLACEHOLDER | + TypeFlags::HAS_CT_PLACEHOLDER } else { TypeFlags::KEEP_IN_LOCAL_TCX | TypeFlags::HAS_RE_PLACEHOLDER | - TypeFlags::HAS_TY_PLACEHOLDER + TypeFlags::HAS_TY_PLACEHOLDER | + TypeFlags::HAS_CT_PLACEHOLDER }; let gcx = tcx.global_tcx(); @@ -633,4 +692,28 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { self.tcx().mk_ty(ty::Bound(self.binder_index, var.into())) } } + + /// Given a type variable `const_var` of the given kind, first check + /// if `const_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `const_var`. + fn canonicalize_const_var( + &mut self, + info: CanonicalVarInfo, + const_var: &'tcx ty::Const<'tcx> + ) -> &'tcx ty::Const<'tcx> { + let infcx = self.infcx.expect("encountered const-var without infcx"); + let bound_to = infcx.resolve_const_var(const_var); + if bound_to != const_var { + self.fold_const(bound_to) + } else { + let var = self.canonical_var(info, const_var.into()); + self.tcx().mk_const( + ty::Const { + val: ConstValue::Infer(InferConst::Canonical(self.binder_index, var.into())), + ty: const_var.ty, + } + ) + } + } } diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index fe6b8ac1cdc7e..7c61db21e613a 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -21,7 +21,8 @@ //! //! [c]: https://rust-lang.github.io/rustc-guide/traits/canonicalization.html -use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin}; +use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, ConstVariableOrigin}; +use crate::mir::interpret::ConstValue; use rustc_data_structures::indexed_vec::IndexVec; use rustc_macros::HashStable; use serialize::UseSpecializedDecodable; @@ -30,7 +31,7 @@ use std::ops::Index; use syntax::source_map::Span; use crate::ty::fold::TypeFoldable; use crate::ty::subst::Kind; -use crate::ty::{self, BoundVar, Lift, List, Region, TyCtxt}; +use crate::ty::{self, BoundVar, InferConst, Lift, List, Region, TyCtxt}; mod canonicalizer; @@ -115,6 +116,8 @@ impl CanonicalVarInfo { CanonicalVarKind::PlaceholderTy(_) => false, CanonicalVarKind::Region(_) => true, CanonicalVarKind::PlaceholderRegion(..) => false, + CanonicalVarKind::Const(_) => true, + CanonicalVarKind::PlaceholderConst(_) => false, } } } @@ -137,6 +140,12 @@ pub enum CanonicalVarKind { /// are solving a goal like `for<'a> T: Foo<'a>` to represent the /// bound region `'a`. PlaceholderRegion(ty::PlaceholderRegion), + + /// Some kind of const inference variable. + Const(ty::UniverseIndex), + + /// A "placeholder" that represents "any const". + PlaceholderConst(ty::PlaceholderConst), } impl CanonicalVarKind { @@ -150,6 +159,8 @@ impl CanonicalVarKind { CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe, CanonicalVarKind::Region(ui) => ui, CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe, + CanonicalVarKind::Const(ui) => ui, + CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe, } } } @@ -388,6 +399,33 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { }; self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into() } + + CanonicalVarKind::Const(ui) => { + self.next_const_var_in_universe( + self.next_ty_var_in_universe( + TypeVariableOrigin::MiscVariable(span), + universe_map(ui), + ), + ConstVariableOrigin::MiscVariable(span), + universe_map(ui), + ).into() + } + + CanonicalVarKind::PlaceholderConst( + ty::PlaceholderConst { universe, name }, + ) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = ty::PlaceholderConst { + universe: universe_mapped, + name, + }; + self.tcx.mk_const( + ty::Const { + val: ConstValue::Placeholder(placeholder_mapped), + ty: self.tcx.types.err, // FIXME(const_generics) + } + ).into() + } } } } @@ -443,8 +481,13 @@ impl<'tcx> CanonicalVarValues<'tcx> { UnpackedKind::Lifetime(..) => tcx.mk_region( ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(i)) ).into(), - UnpackedKind::Const(..) => { - unimplemented!() // FIXME(const_generics) + UnpackedKind::Const(ct) => { + tcx.mk_const(ty::Const { + ty: ct.ty, + val: ConstValue::Infer( + InferConst::Canonical(ty::INNERMOST, ty::BoundVar::from_u32(i)) + ), + }).into() } }) .collect() diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index e605aae0fae0d..8225ed70c5827 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -16,6 +16,7 @@ use crate::infer::canonical::{ use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::InferCtxtBuilder; use crate::infer::{InferCtxt, InferOk, InferResult}; +use crate::mir::interpret::ConstValue; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::IndexVec; use std::fmt::Debug; @@ -25,7 +26,7 @@ use crate::traits::TraitEngine; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; use crate::ty::fold::TypeFoldable; use crate::ty::subst::{Kind, UnpackedKind}; -use crate::ty::{self, BoundVar, Lift, Ty, TyCtxt}; +use crate::ty::{self, BoundVar, InferConst, Lift, Ty, TyCtxt}; use crate::util::captures::Captures; impl<'cx, 'gcx, 'tcx> InferCtxtBuilder<'cx, 'gcx, 'tcx> { @@ -479,8 +480,17 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { opt_values[br.assert_bound_var()] = Some(*original_value); } } - UnpackedKind::Const(..) => { - unimplemented!() // FIXME(const_generics) + UnpackedKind::Const(result_value) => { + if let ty::Const { + val: ConstValue::Infer(InferConst::Canonical(debrujin, b)), + .. + } = result_value { + // ...in which case we would set `canonical_vars[0]` to `Some(const X)`. + + // We only allow a `ty::INNERMOST` index in substitutions. + assert_eq!(*debrujin, ty::INNERMOST); + opt_values[*b] = Some(*original_value); + } } } } diff --git a/src/librustc/infer/canonical/substitute.rs b/src/librustc/infer/canonical/substitute.rs index 5af4e8366818b..6b716d6c3b8ea 100644 --- a/src/librustc/infer/canonical/substitute.rs +++ b/src/librustc/infer/canonical/substitute.rs @@ -70,6 +70,13 @@ where } }; - tcx.replace_escaping_bound_vars(value, fld_r, fld_t).0 + let fld_c = |bound_ct: ty::BoundVar, _| { + match var_values.var_values[bound_ct].unpack() { + UnpackedKind::Const(ct) => ct, + c => bug!("{:?} is a const but value is {:?}", bound_ct, c), + } + }; + + tcx.replace_escaping_bound_vars(value, fld_r, fld_t, fld_c).0 } } diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 885b439ef1ca5..048c0a7a8fd0a 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -28,17 +28,19 @@ use super::{InferCtxt, MiscVariable, TypeTrace}; use super::lub::Lub; use super::sub::Sub; use super::type_variable::TypeVariableValue; +use super::unify_key::{ConstVarValue, ConstVariableValue, ConstVariableOrigin}; use crate::hir::def_id::DefId; +use crate::mir::interpret::ConstValue; use crate::ty::{IntType, UintType}; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt, InferConst}; use crate::ty::error::TypeError; use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; use crate::ty::subst::SubstsRef; use crate::traits::{Obligation, PredicateObligations}; use syntax::ast; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; #[derive(Clone)] pub struct CombineFields<'infcx, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> { @@ -107,13 +109,69 @@ impl<'infcx, 'gcx, 'tcx> InferCtxt<'infcx, 'gcx, 'tcx> { Err(TypeError::Sorts(ty::relate::expected_found(relation, &a, &b))) } - _ => { ty::relate::super_relate_tys(relation, a, b) } } } + pub fn super_combine_consts( + &self, + relation: &mut R, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> + where + R: TypeRelation<'infcx, 'gcx, 'tcx>, + { + let a_is_expected = relation.a_is_expected(); + + match (a.val, b.val) { + (ConstValue::Infer(InferConst::Var(a_vid)), + ConstValue::Infer(InferConst::Var(b_vid))) => { + self.const_unification_table + .borrow_mut() + .unify_var_var(a_vid, b_vid) + .map_err(|e| const_unification_error(a_is_expected, e))?; + return Ok(a); + } + + // All other cases of inference with other variables are errors. + (ConstValue::Infer(InferConst::Var(_)), ConstValue::Infer(_)) | + (ConstValue::Infer(_), ConstValue::Infer(InferConst::Var(_))) => { + bug!("tried to combine ConstValue::Infer/ConstValue::Infer(InferConst::Var)") + } + + (ConstValue::Infer(InferConst::Var(vid)), _) => { + return self.unify_const_variable(a_is_expected, vid, b); + } + + (_, ConstValue::Infer(InferConst::Var(vid))) => { + return self.unify_const_variable(!a_is_expected, vid, a); + } + + _ => {} + } + + ty::relate::super_relate_consts(relation, a, b) + } + + pub fn unify_const_variable( + &self, + vid_is_expected: bool, + vid: ty::ConstVid<'tcx>, + value: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + self.const_unification_table + .borrow_mut() + .unify_var_value(vid, ConstVarValue { + origin: ConstVariableOrigin::ConstInference(DUMMY_SP), + val: ConstVariableValue::Known { value }, + }) + .map_err(|e| const_unification_error(vid_is_expected, e))?; + Ok(value) + } + fn unify_integral_variable(&self, vid_is_expected: bool, vid: ty::IntVid, @@ -407,7 +465,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, ' debug!("generalize: t={:?}", t); - // Check to see whether the type we are genealizing references + // Check to see whether the type we are generalizing references // any other type variable related to `vid` via // subtyping. This is basically our "occurs check", preventing // us from creating infinitely sized types. @@ -519,6 +577,29 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, ' // very descriptive origin for this region variable. Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe)) } + + fn consts( + &mut self, + c: &'tcx ty::Const<'tcx>, + c2: &'tcx ty::Const<'tcx> + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + assert_eq!(c, c2); // we are abusing TypeRelation here; both LHS and RHS ought to be == + + match c { + ty::Const { val: ConstValue::Infer(InferConst::Var(vid)), .. } => { + let mut variable_table = self.infcx.const_unification_table.borrow_mut(); + match variable_table.probe_value(*vid).val.known() { + Some(u) => { + self.relate(&u, &u) + } + None => Ok(c), + } + } + _ => { + relate::super_relate_consts(self, c, c) + } + } + } } pub trait RelateResultCompare<'tcx, T> { @@ -540,6 +621,13 @@ impl<'tcx, T:Clone + PartialEq> RelateResultCompare<'tcx, T> for RelateResult<'t } } +pub fn const_unification_error<'tcx>( + a_is_expected: bool, + (a, b): (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>), +) -> TypeError<'tcx> { + TypeError::ConstMismatch(ty::relate::expected_found_bool(a_is_expected, &a, &b)) +} + fn int_unification_error<'tcx>(a_is_expected: bool, v: (ty::IntVarValue, ty::IntVarValue)) -> TypeError<'tcx> { diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index 31b01eecf5cb6..f61408696ecbf 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs @@ -1,12 +1,14 @@ -use super::combine::{CombineFields, RelationDir}; -use super::{Subtype}; +use super::combine::{CombineFields, RelationDir, const_unification_error}; +use super::Subtype; use crate::hir::def_id::DefId; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt, InferConst}; use crate::ty::TyVar; use crate::ty::subst::SubstsRef; use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use crate::mir::interpret::ConstValue; +use crate::infer::unify_key::replace_if_possible; /// Ensures `a` is made equal to `b`. Returns `a` on success. pub struct Equate<'combine, 'infcx: 'combine, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> { @@ -100,6 +102,46 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> Ok(a) } + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug!("{}.consts({:?}, {:?})", self.tag(), a, b); + if a == b { return Ok(a); } + + let infcx = self.fields.infcx; + let a = replace_if_possible(infcx.const_unification_table.borrow_mut(), a); + let b = replace_if_possible(infcx.const_unification_table.borrow_mut(), b); + let a_is_expected = self.a_is_expected(); + + match (a.val, b.val) { + (ConstValue::Infer(InferConst::Var(a_vid)), + ConstValue::Infer(InferConst::Var(b_vid))) => { + infcx.const_unification_table + .borrow_mut() + .unify_var_var(a_vid, b_vid) + .map_err(|e| const_unification_error(a_is_expected, e))?; + return Ok(a); + } + + (ConstValue::Infer(InferConst::Var(a_id)), _) => { + self.fields.infcx.unify_const_variable(a_is_expected, a_id, b)?; + return Ok(a); + } + + (_, ConstValue::Infer(InferConst::Var(b_id))) => { + self.fields.infcx.unify_const_variable(!a_is_expected, b_id, a)?; + return Ok(a); + } + + _ => {} + } + + self.fields.infcx.super_combine_consts(self, a, b)?; + Ok(a) + } + fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) -> RelateResult<'tcx, ty::Binder> where T: Relate<'tcx> diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 27ed36ab98b0f..679635bef13e5 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -31,6 +31,7 @@ //! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type //! inferencer knows "so far". +use crate::mir::interpret::ConstValue; use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; use crate::ty::fold::TypeFolder; use crate::util::nodemap::FxHashMap; @@ -42,8 +43,10 @@ use super::unify_key::ToType; pub struct TypeFreshener<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - freshen_count: u32, - freshen_map: FxHashMap>, + ty_freshen_count: u32, + const_freshen_count: u32, + ty_freshen_map: FxHashMap>, + const_freshen_map: FxHashMap, &'tcx ty::Const<'tcx>>, } impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> { @@ -51,33 +54,63 @@ impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> { -> TypeFreshener<'a, 'gcx, 'tcx> { TypeFreshener { infcx, - freshen_count: 0, - freshen_map: Default::default(), + ty_freshen_count: 0, + const_freshen_count: 0, + ty_freshen_map: Default::default(), + const_freshen_map: Default::default(), } } - fn freshen(&mut self, - opt_ty: Option>, - key: ty::InferTy, - freshener: F) - -> Ty<'tcx> where + fn freshen_ty( + &mut self, + opt_ty: Option>, + key: ty::InferTy, + freshener: F, + ) -> Ty<'tcx> + where F: FnOnce(u32) -> ty::InferTy, { if let Some(ty) = opt_ty { return ty.fold_with(self); } - match self.freshen_map.entry(key) { + match self.ty_freshen_map.entry(key) { Entry::Occupied(entry) => *entry.get(), Entry::Vacant(entry) => { - let index = self.freshen_count; - self.freshen_count += 1; - let t = self.infcx.tcx.mk_infer(freshener(index)); + let index = self.ty_freshen_count; + self.ty_freshen_count += 1; + let t = self.infcx.tcx.mk_ty_infer(freshener(index)); entry.insert(t); t } } } + + fn freshen_const( + &mut self, + opt_ct: Option<&'tcx ty::Const<'tcx>>, + key: ty::InferConst<'tcx>, + freshener: F, + ty: Ty<'tcx>, + ) -> &'tcx ty::Const<'tcx> + where + F: FnOnce(u32) -> ty::InferConst<'tcx>, + { + if let Some(ct) = opt_ct { + return ct.fold_with(self); + } + + match self.const_freshen_map.entry(key) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + let index = self.const_freshen_count; + self.const_freshen_count += 1; + let ct = self.infcx.tcx.mk_const_infer(freshener(index), ty); + entry.insert(ct); + ct + } + } + } } impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { @@ -124,14 +157,14 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { match t.sty { ty::Infer(ty::TyVar(v)) => { let opt_ty = self.infcx.type_variables.borrow_mut().probe(v).known(); - self.freshen( + self.freshen_ty( opt_ty, ty::TyVar(v), ty::FreshTy) } ty::Infer(ty::IntVar(v)) => { - self.freshen( + self.freshen_ty( self.infcx.int_unification_table.borrow_mut() .probe_value(v) .map(|v| v.to_type(tcx)), @@ -140,7 +173,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { } ty::Infer(ty::FloatVar(v)) => { - self.freshen( + self.freshen_ty( self.infcx.float_unification_table.borrow_mut() .probe_value(v) .map(|v| v.to_type(tcx)), @@ -148,14 +181,14 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { ty::FreshFloatTy) } - ty::Infer(ty::FreshTy(c)) | - ty::Infer(ty::FreshIntTy(c)) | - ty::Infer(ty::FreshFloatTy(c)) => { - if c >= self.freshen_count { + ty::Infer(ty::FreshTy(ct)) | + ty::Infer(ty::FreshIntTy(ct)) | + ty::Infer(ty::FreshFloatTy(ct)) => { + if ct >= self.ty_freshen_count { bug!("Encountered a freshend type with id {} \ but our counter is only at {}", - c, - self.freshen_count); + ct, + self.ty_freshen_count); } t } @@ -192,4 +225,46 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { ty::Bound(..) => bug!("unexpected type {:?}", t), } } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + match ct.val { + ConstValue::Infer(ty::InferConst::Var(v)) => { + let opt_ct = self.infcx.const_unification_table + .borrow_mut() + .probe_value(v) + .val + .known(); + return self.freshen_const( + opt_ct, + ty::InferConst::Var(v), + ty::InferConst::Fresh, + ct.ty, + ); + } + ConstValue::Infer(ty::InferConst::Fresh(i)) => { + if i >= self.const_freshen_count { + bug!( + "Encountered a freshend const with id {} \ + but our counter is only at {}", + i, + self.const_freshen_count, + ); + } + return ct; + } + + ConstValue::Infer(ty::InferConst::Canonical(..)) | + ConstValue::Placeholder(_) => { + bug!("unexpected const {:?}", ct) + } + + ConstValue::Param(_) | + ConstValue::Scalar(_) | + ConstValue::Slice(..) | + ConstValue::ByRef(..) | + ConstValue::Unevaluated(..) => {} + } + + ct.super_fold_with(self) + } } diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index 7788ae2b88f21..5f5a2d4a489e3 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -1,12 +1,27 @@ -use crate::ty::{self, Ty, TyCtxt, TyVid, IntVid, FloatVid, RegionVid}; +use crate::ty::{self, Ty, TyCtxt, TyVid, IntVid, FloatVid, RegionVid, ConstVid}; use crate::ty::fold::{TypeFoldable, TypeFolder}; +use crate::mir::interpret::ConstValue; use super::InferCtxt; -use super::RegionVariableOrigin; +use super::{RegionVariableOrigin, ConstVariableOrigin}; use super::type_variable::TypeVariableOrigin; +use rustc_data_structures::unify as ut; +use ut::UnifyKey; + +use std::cell::RefMut; use std::ops::Range; +fn const_vars_since_snapshot<'tcx>( + mut table: RefMut<'_, ut::UnificationTable>>>, + snapshot: &ut::Snapshot>>, +) -> (Range>, Vec) { + let range = table.vars_since_snapshot(snapshot); + (range.start..range.end, (range.start.index..range.end.index).map(|index| { + table.probe_value(ConstVid::from_index(index)).origin.clone() + }).collect()) +} + impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// This rather funky routine is used while processing expected /// types. What happens here is that we want to propagate a @@ -79,6 +94,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let region_vars = self.borrow_region_constraints().vars_since_snapshot( &snapshot.region_constraints_snapshot, ); + let const_vars = const_vars_since_snapshot( + self.const_unification_table.borrow_mut(), + &snapshot.const_snapshot, + ); let fudger = InferenceFudger { infcx: self, @@ -86,6 +105,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { int_vars, float_vars, region_vars, + const_vars, }; Ok((fudger, value)) @@ -104,7 +124,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { if fudger.type_vars.0.is_empty() && fudger.int_vars.is_empty() && fudger.float_vars.is_empty() && - fudger.region_vars.0.is_empty() { + fudger.region_vars.0.is_empty() && + fudger.const_vars.0.is_empty() { Ok(value) } else { Ok(value.fold_with(&mut fudger)) @@ -118,6 +139,7 @@ pub struct InferenceFudger<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { int_vars: Range, float_vars: Range, region_vars: (Range, Vec), + const_vars: (Range>, Vec), } impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for InferenceFudger<'a, 'gcx, 'tcx> { @@ -165,13 +187,29 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for InferenceFudger<'a, 'gcx, 'tcx> } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReVar(vid) = r { + if let ty::ReVar(vid) = *r { if self.region_vars.0.contains(&vid) { - let idx = (vid.index() - self.region_vars.0.start.index()) as usize; + let idx = vid.index() - self.region_vars.0.start.index(); let origin = self.region_vars.1[idx]; return self.infcx.next_region_var(origin); } } r } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if let ty::Const { val: ConstValue::Infer(ty::InferConst::Var(vid)), ty } = ct { + if self.const_vars.0.contains(&vid) { + // This variable was created during the fudging. + // Recreate it with a fresh variable here. + let idx = (vid.index - self.const_vars.0.start.index) as usize; + let origin = self.const_vars.1[idx]; + self.infcx.next_const_var(ty, origin) + } else { + ct + } + } else { + ct.super_fold_with(self) + } + } } diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index 910c6571853dc..63a8f17398a04 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -60,6 +60,19 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> Ok(self.fields.infcx.borrow_region_constraints().glb_regions(self.tcx(), origin, a, b)) } + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug!("{}.consts({:?}, {:?})", self.tag(), a, b); + if a == b { + return Ok(a); + } + + self.fields.infcx.super_combine_consts(self, a, b) + } + fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) -> RelateResult<'tcx, ty::Binder> where T: Relate<'tcx> diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 7c83fe7fd6946..fcec820cbb92a 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -7,6 +7,7 @@ use super::{HigherRankedType, InferCtxt, PlaceholderMap}; use crate::infer::CombinedSnapshot; use crate::ty::relate::{Relate, RelateResult, TypeRelation}; use crate::ty::{self, Binder, TypeFoldable}; +use crate::mir::interpret::ConstValue; impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { pub fn higher_ranked_sub( @@ -99,7 +100,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { })) }; - let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t); + let fld_c = |bound_var: ty::BoundVar, ty| { + self.tcx.mk_const( + ty::Const { + val: ConstValue::Placeholder(ty::PlaceholderConst { + universe: next_universe, + name: bound_var, + }), + ty, + } + ) + }; + + let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t, fld_c); debug!( "replace_bound_vars_with_placeholders(\ diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index f9eb60d82d17b..29b319ef8f5ff 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -60,6 +60,19 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> Ok(self.fields.infcx.borrow_region_constraints().lub_regions(self.tcx(), origin, a, b)) } + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug!("{}.consts({:?}, {:?})", self.tag(), a, b); + if a == b { + return Ok(a); + } + + self.fields.infcx.super_combine_consts(self, a, b) + } + fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) -> RelateResult<'tcx, ty::Binder> where T: Relate<'tcx> diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index e9b80c56e0c5c..65bc2b3e945ce 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -10,17 +10,19 @@ pub use crate::ty::IntVarValue; use crate::hir; use crate::hir::def_id::DefId; use crate::infer::canonical::{Canonical, CanonicalVarValues}; +use crate::infer::unify_key::{ConstVarValue, ConstVariableValue}; use crate::middle::free_region::RegionRelations; use crate::middle::lang_items; use crate::middle::region; +use crate::mir::interpret::ConstValue; use crate::session::config::BorrowckMode; use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; use crate::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; -use crate::ty::fold::TypeFoldable; +use crate::ty::fold::{TypeFolder, TypeFoldable}; use crate::ty::relate::RelateResult; use crate::ty::subst::{Kind, InternalSubsts, SubstsRef}; -use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt, CtxtInterners}; -use crate::ty::{FloatVid, IntVid, TyVid}; +use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt, CtxtInterners, InferConst}; +use crate::ty::{FloatVid, IntVid, TyVid, ConstVid}; use crate::util::nodemap::FxHashMap; use arena::SyncDroplessArena; @@ -39,7 +41,7 @@ use self::outlives::env::OutlivesEnvironment; use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, VerifyBound}; use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; use self::type_variable::TypeVariableOrigin; -use self::unify_key::ToType; +use self::unify_key::{ToType, ConstVariableOrigin}; pub mod at; pub mod canonical; @@ -72,7 +74,7 @@ pub type InferResult<'tcx, T> = Result, TypeError<'tcx>>; pub type Bound = Option; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" -pub type FixupResult = Result; // "fixup result" +pub type FixupResult<'tcx, T> = Result>; // "fixup result" /// A flag that is used to suppress region errors. This is normally /// false, but sometimes -- when we are doing region checks that the @@ -122,7 +124,10 @@ pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { /// order, represented by its upper and lower bounds. pub type_variables: RefCell>, - /// Map from integral variable to the kind of integer it represents + /// Map from const parameter variable to the kind of const it represents. + const_unification_table: RefCell>>>, + + /// Map from integral variable to the kind of integer it represents. int_unification_table: RefCell>>, /// Map from floating variable to the kind of float it represents @@ -422,10 +427,11 @@ impl NLLRegionVariableOrigin { } #[derive(Copy, Clone, Debug)] -pub enum FixupError { +pub enum FixupError<'tcx> { UnresolvedIntTy(IntVid), UnresolvedFloatTy(FloatVid), UnresolvedTy(TyVid), + UnresolvedConst(ConstVid<'tcx>), } /// See the `region_obligations` field for more information. @@ -436,7 +442,7 @@ pub struct RegionObligation<'tcx> { pub origin: SubregionOrigin<'tcx>, } -impl fmt::Display for FixupError { +impl<'tcx> fmt::Display for FixupError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::FixupError::*; @@ -452,6 +458,7 @@ impl fmt::Display for FixupError { add a suffix to specify the type explicitly" ), UnresolvedTy(_) => write!(f, "unconstrained type"), + UnresolvedConst(_) => write!(f, "unconstrained const value"), } } } @@ -524,6 +531,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { in_progress_tables, projection_cache: Default::default(), type_variables: RefCell::new(type_variable::TypeVariableTable::new()), + const_unification_table: RefCell::new(ut::UnificationTable::new()), int_unification_table: RefCell::new(ut::UnificationTable::new()), float_unification_table: RefCell::new(ut::UnificationTable::new()), region_constraints: RefCell::new(Some(RegionConstraintCollector::new())), @@ -589,6 +597,7 @@ impl<'tcx> InferOk<'tcx, ()> { pub struct CombinedSnapshot<'a, 'tcx: 'a> { projection_cache_snapshot: traits::ProjectionCacheSnapshot, type_snapshot: type_variable::Snapshot<'tcx>, + const_snapshot: ut::Snapshot>>, int_snapshot: ut::Snapshot>, float_snapshot: ut::Snapshot>, region_constraints_snapshot: RegionSnapshot, @@ -652,6 +661,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let mut type_variables = self.type_variables.borrow_mut(); let mut int_unification_table = self.int_unification_table.borrow_mut(); let mut float_unification_table = self.float_unification_table.borrow_mut(); + // FIXME(const_generics): should there be an equivalent function for const variables? type_variables .unsolved_variables() @@ -722,6 +732,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { CombinedSnapshot { projection_cache_snapshot: self.projection_cache.borrow_mut().snapshot(), type_snapshot: self.type_variables.borrow_mut().snapshot(), + const_snapshot: self.const_unification_table.borrow_mut().snapshot(), int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), region_constraints_snapshot: self.borrow_region_constraints().start_snapshot(), @@ -739,6 +750,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let CombinedSnapshot { projection_cache_snapshot, type_snapshot, + const_snapshot, int_snapshot, float_snapshot, region_constraints_snapshot, @@ -751,21 +763,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.in_snapshot.set(was_in_snapshot); self.universe.set(universe); - self.projection_cache - .borrow_mut() - .rollback_to(projection_cache_snapshot); + self.projection_cache.borrow_mut().rollback_to(projection_cache_snapshot); self.type_variables.borrow_mut().rollback_to(type_snapshot); - self.int_unification_table - .borrow_mut() - .rollback_to(int_snapshot); - self.float_unification_table - .borrow_mut() - .rollback_to(float_snapshot); - self.region_obligations - .borrow_mut() - .truncate(region_obligations_snapshot); - self.borrow_region_constraints() - .rollback_to(region_constraints_snapshot); + self.const_unification_table.borrow_mut().rollback_to(const_snapshot); + self.int_unification_table.borrow_mut().rollback_to(int_snapshot); + self.float_unification_table.borrow_mut().rollback_to(float_snapshot); + self.region_obligations.borrow_mut().truncate(region_obligations_snapshot); + self.borrow_region_constraints().rollback_to(region_constraints_snapshot); } fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) { @@ -773,6 +777,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let CombinedSnapshot { projection_cache_snapshot, type_snapshot, + const_snapshot, int_snapshot, float_snapshot, region_constraints_snapshot, @@ -784,16 +789,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.in_snapshot.set(was_in_snapshot); - self.projection_cache - .borrow_mut() - .commit(projection_cache_snapshot); + self.projection_cache.borrow_mut().commit(projection_cache_snapshot); self.type_variables.borrow_mut().commit(type_snapshot); + self.const_unification_table.borrow_mut().commit(const_snapshot); self.int_unification_table.borrow_mut().commit(int_snapshot); - self.float_unification_table - .borrow_mut() - .commit(float_snapshot); - self.borrow_region_constraints() - .commit(region_constraints_snapshot); + self.float_unification_table.borrow_mut().commit(float_snapshot); + self.borrow_region_constraints().commit(region_constraints_snapshot); } /// Executes `f` and commit the bindings. @@ -999,6 +1000,38 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tcx.mk_ty_var(self.next_ty_var_id(true, origin)) } + pub fn next_const_var( + &self, + ty: Ty<'tcx>, + origin: ConstVariableOrigin + ) -> &'tcx ty::Const<'tcx> { + self.tcx.mk_const_var(self.next_const_var_id(origin), ty) + } + + pub fn next_const_var_in_universe( + &self, + ty: Ty<'tcx>, + origin: ConstVariableOrigin, + universe: ty::UniverseIndex, + ) -> &'tcx ty::Const<'tcx> { + let vid = self.const_unification_table + .borrow_mut() + .new_key(ConstVarValue { + origin, + val: ConstVariableValue::Unknown { universe }, + }); + self.tcx.mk_const_var(vid, ty) + } + + pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> { + self.const_unification_table + .borrow_mut() + .new_key(ConstVarValue { + origin, + val: ConstVariableValue::Unknown { universe: self.universe() }, + }) + } + fn next_int_var_id(&self) -> IntVid { self.int_unification_table.borrow_mut().new_key(None) } @@ -1092,7 +1125,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tcx.mk_ty_var(ty_var_id).into() } GenericParamDefKind::Const { .. } => { - unimplemented!() // FIXME(const_generics) + let origin = ConstVariableOrigin::ConstParameterDefinition(span, param.name); + let const_var_id = + self.const_unification_table + .borrow_mut() + .new_key(ConstVarValue { + origin, + val: ConstVariableValue::Unknown { universe: self.universe() }, + }); + self.tcx.mk_const_var(const_var_id, self.tcx.type_of(param.def_id)).into() } } } @@ -1233,46 +1274,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.resolve_type_vars_if_possible(t).to_string() } - // We have this force-inlined variant of shallow_resolve() for the one - // callsite that is extremely hot. All other callsites use the normal - // variant. - #[inline(always)] - pub fn inlined_shallow_resolve(&self, typ: Ty<'tcx>) -> Ty<'tcx> { - match typ.sty { - ty::Infer(ty::TyVar(v)) => { - // Not entirely obvious: if `typ` is a type variable, - // it can be resolved to an int/float variable, which - // can then be recursively resolved, hence the - // recursion. Note though that we prevent type - // variables from unifyxing to other type variables - // directly (though they may be embedded - // structurally), and we prevent cycles in any case, - // so this recursion should always be of very limited - // depth. - self.type_variables - .borrow_mut() - .probe(v) - .known() - .map(|t| self.shallow_resolve(t)) - .unwrap_or(typ) - } - - ty::Infer(ty::IntVar(v)) => self.int_unification_table - .borrow_mut() - .probe_value(v) - .map(|v| v.to_type(self.tcx)) - .unwrap_or(typ), - - ty::Infer(ty::FloatVar(v)) => self.float_unification_table - .borrow_mut() - .probe_value(v) - .map(|v| v.to_type(self.tcx)) - .unwrap_or(typ), - - _ => typ, - } - } - /// If `TyVar(vid)` resolves to a type, return that type. Else, return the /// universe index of `TyVar(vid)`. pub fn probe_ty_var(&self, vid: TyVid) -> Result, ty::UniverseIndex> { @@ -1284,8 +1285,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - pub fn shallow_resolve(&self, typ: Ty<'tcx>) -> Ty<'tcx> { - self.inlined_shallow_resolve(typ) + pub fn shallow_resolve(&self, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + let mut r = ShallowResolver::new(self); + value.fold_with(&mut r) } pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid { @@ -1323,9 +1328,36 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r.first_unresolved } - pub fn fully_resolve>(&self, value: &T) -> FixupResult { + pub fn probe_const_var( + &self, + vid: ty::ConstVid<'tcx> + ) -> Result<&'tcx ty::Const<'tcx>, ty::UniverseIndex> { + match self.const_unification_table.borrow_mut().probe_value(vid).val { + ConstVariableValue::Known { value } => Ok(value), + ConstVariableValue::Unknown { universe } => Err(universe), + } + } + + pub fn resolve_const_var( + &self, + ct: &'tcx ty::Const<'tcx> + ) -> &'tcx ty::Const<'tcx> { + if let ty::Const { val: ConstValue::Infer(InferConst::Var(v)), .. } = ct { + self.const_unification_table + .borrow_mut() + .probe_value(*v) + .val + .known() + .map(|c| self.resolve_const_var(c)) + .unwrap_or(ct) + } else { + ct + } + } + + pub fn fully_resolve>(&self, value: &T) -> FixupResult<'tcx, T> { /*! - * Attempts to resolve all type/region variables in + * 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 * variable was never unified, an `Err` results. @@ -1390,7 +1422,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { let fld_r = |br| self.next_region_var(LateBoundRegion(span, br, lbrct)); let fld_t = |_| self.next_ty_var(TypeVariableOrigin::MiscVariable(span)); - self.tcx.replace_bound_vars(value, fld_r, fld_t) + let fld_c = |_, ty| self.next_const_var(ty, ConstVariableOrigin::MiscVariable(span)); + self.tcx.replace_bound_vars(value, fld_r, fld_t, fld_c) } /// See the [`region_constraints::verify_generic_bound`] method. @@ -1441,7 +1474,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { closure_substs: ty::ClosureSubsts<'tcx>, ) -> Option { let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self.tcx); - let closure_kind_ty = self.shallow_resolve(&closure_kind_ty); + let closure_kind_ty = self.shallow_resolve(closure_kind_ty); closure_kind_ty.to_opt_closure_kind() } @@ -1455,7 +1488,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { substs: ty::ClosureSubsts<'tcx>, ) -> ty::PolyFnSig<'tcx> { let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx); - let closure_sig_ty = self.shallow_resolve(&closure_sig_ty); + let closure_sig_ty = self.shallow_resolve(closure_sig_ty); closure_sig_ty.fn_sig(self.tcx) } @@ -1511,6 +1544,82 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } +pub struct ShallowResolver<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, +} + +impl<'a, 'gcx, 'tcx> ShallowResolver<'a, 'gcx, 'tcx> { + #[inline(always)] + pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { + ShallowResolver { infcx } + } + + // We have this force-inlined variant of `shallow_resolve` for the one + // callsite that is extremely hot. All other callsites use the normal + // variant. + #[inline(always)] + pub fn inlined_shallow_resolve(&mut self, typ: Ty<'tcx>) -> Ty<'tcx> { + match typ.sty { + ty::Infer(ty::TyVar(v)) => { + // Not entirely obvious: if `typ` is a type variable, + // it can be resolved to an int/float variable, which + // can then be recursively resolved, hence the + // recursion. Note though that we prevent type + // variables from unifyxing to other type variables + // directly (though they may be embedded + // structurally), and we prevent cycles in any case, + // so this recursion should always be of very limited + // depth. + self.infcx.type_variables + .borrow_mut() + .probe(v) + .known() + .map(|t| self.fold_ty(t)) + .unwrap_or(typ) + } + + ty::Infer(ty::IntVar(v)) => self.infcx.int_unification_table + .borrow_mut() + .probe_value(v) + .map(|v| v.to_type(self.infcx.tcx)) + .unwrap_or(typ), + + ty::Infer(ty::FloatVar(v)) => self.infcx.float_unification_table + .borrow_mut() + .probe_value(v) + .map(|v| v.to_type(self.infcx.tcx)) + .unwrap_or(typ), + + _ => typ, + } + } +} + +impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ShallowResolver<'a, 'gcx, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.inlined_shallow_resolve(ty) + } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + match ct { + ty::Const { val: ConstValue::Infer(InferConst::Var(vid)), .. } => { + self.infcx.const_unification_table + .borrow_mut() + .probe_value(*vid) + .val + .known() + .map(|c| self.fold_const(c)) + .unwrap_or(ct) + } + _ => ct, + } + } +} + impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> { pub fn span(&self) -> Span { self.cause.span diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs index 753fd04aac38a..a3800d8f0754e 100644 --- a/src/librustc/infer/nll_relate/mod.rs +++ b/src/librustc/infer/nll_relate/mod.rs @@ -27,7 +27,8 @@ use crate::ty::error::TypeError; use crate::ty::fold::{TypeFoldable, TypeVisitor}; use crate::ty::relate::{self, Relate, RelateResult, TypeRelation}; use crate::ty::subst::Kind; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt, InferConst}; +use crate::mir::interpret::ConstValue; use rustc_data_structures::fx::FxHashMap; use std::fmt::Debug; @@ -608,6 +609,21 @@ where Ok(a) } + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + if let ty::Const { val: ConstValue::Infer(InferConst::Canonical(_, _)), .. } = a { + // FIXME(const_generics): I'm unsure how this branch should actually be handled, + // so this is probably not correct. + self.infcx.super_combine_consts(self, a, b) + } else { + debug!("consts(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance); + relate::super_relate_consts(self, a, b) + } + } + fn binders( &mut self, a: &ty::Binder, @@ -853,7 +869,7 @@ where fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { use crate::infer::type_variable::TypeVariableValue; - debug!("TypeGeneralizer::tys(a={:?})", a,); + debug!("TypeGeneralizer::tys(a={:?})", a); match a.sty { ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) @@ -934,7 +950,7 @@ where a: ty::Region<'tcx>, _: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("TypeGeneralizer::regions(a={:?})", a,); + debug!("TypeGeneralizer::regions(a={:?})", a); if let ty::ReLateBound(debruijn, _) = a { if *debruijn < self.first_free_index { @@ -963,6 +979,23 @@ where Ok(replacement_region_vid) } + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + _: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug!("TypeGeneralizer::consts(a={:?})", a); + + if let ty::Const { val: ConstValue::Infer(InferConst::Canonical(_, _)), .. } = a { + bug!( + "unexpected inference variable encountered in NLL generalization: {:?}", + a + ); + } else { + relate::super_relate_consts(self, a, a) + } + } + fn binders( &mut self, a: &ty::Binder, @@ -971,7 +1004,7 @@ where where T: Relate<'tcx>, { - debug!("TypeGeneralizer::binders(a={:?})", a,); + debug!("TypeGeneralizer::binders(a={:?})", a); self.first_free_index.shift_in(1); let result = self.relate(a.skip_binder(), a.skip_binder())?; diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index b1d009146473f..6d8558211818b 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -676,8 +676,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { let tcx = self.infcx.tcx; value.fold_with(&mut BottomUpFolder { tcx, - reg_op: |reg| reg, - fldop: |ty| { + ty_op: |ty| { if let ty::Opaque(def_id, substs) = ty.sty { // Check that this is `impl Trait` type is // declared by `parent_def_id` -- i.e., one whose @@ -776,6 +775,8 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { ty }, + lt_op: |lt| lt, + ct_op: |ct| ct, }) } diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 6adbf2bcef866..079385368f883 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -1,5 +1,6 @@ use super::{InferCtxt, FixupError, FixupResult, Span, type_variable::TypeVariableOrigin}; -use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; +use crate::mir::interpret::ConstValue; +use crate::ty::{self, Ty, TyCtxt, TypeFoldable, InferConst}; use crate::ty::fold::{TypeFolder, TypeVisitor}; /////////////////////////////////////////////////////////////////////////// @@ -72,6 +73,15 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv r, } } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if !ct.needs_infer() { + ct // micro-optimize -- if there is nothing in this const that this fold affects... + } else { + let c0 = self.infcx.shallow_resolve(ct); + c0.super_fold_with(self) + } + } } /////////////////////////////////////////////////////////////////////////// @@ -136,7 +146,7 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'gcx, 'tcx> /// their concrete results. If any variable cannot be replaced (never unified, etc) /// then an `Err` result is returned. pub fn fully_resolve<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - value: &T) -> FixupResult + value: &T) -> FixupResult<'tcx, T> where T : TypeFoldable<'tcx> { let mut full_resolver = FullTypeResolver { infcx: infcx, err: None }; @@ -151,7 +161,7 @@ pub fn fully_resolve<'a, 'gcx, 'tcx, T>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, // `err` field is not enforcable otherwise. struct FullTypeResolver<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - err: Option, + err: Option>, } impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for FullTypeResolver<'a, 'gcx, 'tcx> { @@ -199,4 +209,25 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for FullTypeResolver<'a, 'gcx, 'tcx> _ => r, } } + + fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if !c.needs_infer() && !ty::keep_local(&c) { + c // micro-optimize -- if there is nothing in this const that this fold affects... + // ^ we need to have the `keep_local` check to un-default + // defaulted tuples. + } else { + let c = self.infcx.shallow_resolve(c); + match c.val { + ConstValue::Infer(InferConst::Var(vid)) => { + self.err = Some(FixupError::UnresolvedConst(vid)); + return self.tcx().consts.err; + } + ConstValue::Infer(InferConst::Fresh(_)) => { + bug!("Unexpected const in full const resolver: {:?}", c); + } + _ => {} + } + c.super_fold_with(self) + } + } } diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index 0cff42742c30a..f2f36d59cbef6 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -1,11 +1,13 @@ use super::SubregionOrigin; -use super::combine::{CombineFields, RelationDir}; +use super::combine::{CombineFields, RelationDir, const_unification_error}; use crate::traits::Obligation; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt, InferConst}; use crate::ty::TyVar; use crate::ty::fold::TypeFoldable; use crate::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; +use crate::infer::unify_key::replace_if_possible; +use crate::mir::interpret::ConstValue; use std::mem; /// Ensures `a` is made a subtype of `b`. Returns `a` on success. @@ -133,6 +135,48 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> Ok(a) } + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug!("{}.consts({:?}, {:?})", self.tag(), a, b); + if a == b { return Ok(a); } + + let infcx = self.fields.infcx; + let a = replace_if_possible(infcx.const_unification_table.borrow_mut(), a); + let b = replace_if_possible(infcx.const_unification_table.borrow_mut(), b); + + // Consts can only be equal or unequal to each other: there's no subtyping + // relation, so we're just going to perform equating here instead. + let a_is_expected = self.a_is_expected(); + match (a.val, b.val) { + (ConstValue::Infer(InferConst::Var(a_vid)), + ConstValue::Infer(InferConst::Var(b_vid))) => { + infcx.const_unification_table + .borrow_mut() + .unify_var_var(a_vid, b_vid) + .map_err(|e| const_unification_error(a_is_expected, e))?; + return Ok(a); + } + + (ConstValue::Infer(InferConst::Var(a_id)), _) => { + self.fields.infcx.unify_const_variable(a_is_expected, a_id, b)?; + return Ok(a); + } + + (_, ConstValue::Infer(InferConst::Var(b_id))) => { + self.fields.infcx.unify_const_variable(!a_is_expected, b_id, a)?; + return Ok(a); + } + + _ => {} + } + + self.fields.infcx.super_combine_consts(self, a, b)?; + Ok(a) + } + fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) -> RelateResult<'tcx, ty::Binder> where T: Relate<'tcx> diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index 1393e4f67eb92..88933312b0d40 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -17,7 +17,7 @@ pub struct TypeVariableTable<'tcx> { /// the known value. eq_relations: ut::UnificationTable>>, - /// Two variables are unified in `eq_relations` when we have a + /// Two variables are unified in `sub_relations` when we have a /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second /// table exists only to help with the occurs check. In particular, /// we want to report constraints like these as an occurs check @@ -365,7 +365,7 @@ impl sv::SnapshotVecDelegate for Delegate { fn reverse(_values: &mut Vec, _action: Instantiate) { // We don't actually have to *do* anything to reverse an - // instanation; the value for a variable is stored in the + // instantiation; the value for a variable is stored in the // `eq_relations` and hence its rollback code will handle // it. In fact, we could *almost* just remove the // `SnapshotVec` entirely, except that we would have to diff --git a/src/librustc/infer/unify_key.rs b/src/librustc/infer/unify_key.rs index 09f800d9f9bfc..678a45a84e9ea 100644 --- a/src/librustc/infer/unify_key.rs +++ b/src/librustc/infer/unify_key.rs @@ -1,5 +1,13 @@ -use crate::ty::{self, FloatVarValue, IntVarValue, Ty, TyCtxt}; -use rustc_data_structures::unify::{NoError, EqUnifyValue, UnifyKey, UnifyValue}; +use crate::ty::{self, FloatVarValue, IntVarValue, Ty, TyCtxt, InferConst}; +use crate::mir::interpret::ConstValue; +use rustc_data_structures::unify::{NoError, EqUnifyValue, UnifyKey, UnifyValue, UnificationTable}; +use rustc_data_structures::unify::InPlace; +use syntax_pos::{Span, DUMMY_SP}; +use syntax::symbol::InternedString; + +use std::cmp; +use std::marker::PhantomData; +use std::cell::RefMut; pub trait ToType { fn to_type<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx>; @@ -12,8 +20,7 @@ impl UnifyKey for ty::IntVid { fn tag() -> &'static str { "IntVid" } } -impl EqUnifyValue for IntVarValue { -} +impl EqUnifyValue for IntVarValue {} #[derive(PartialEq, Copy, Clone, Debug)] pub struct RegionVidKey { @@ -62,11 +69,114 @@ impl UnifyKey for ty::FloatVid { fn tag() -> &'static str { "FloatVid" } } -impl EqUnifyValue for FloatVarValue { -} +impl EqUnifyValue for FloatVarValue {} impl ToType for FloatVarValue { fn to_type<'a, 'gcx, 'tcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { tcx.mk_mach_float(self.0) } } + +// Generic consts. + +/// Reasons to create a const inference variable +#[derive(Copy, Clone, Debug)] +pub enum ConstVariableOrigin { + MiscVariable(Span), + ConstInference(Span), + ConstParameterDefinition(Span, InternedString), + SubstitutionPlaceholder(Span), +} + +#[derive(Copy, Clone, Debug)] +pub enum ConstVariableValue<'tcx> { + Known { value: &'tcx ty::Const<'tcx> }, + Unknown { universe: ty::UniverseIndex }, +} + +impl<'tcx> ConstVariableValue<'tcx> { + /// If this value is known, returns the const it is known to be. + /// Otherwise, `None`. + pub fn known(&self) -> Option<&'tcx ty::Const<'tcx>> { + match *self { + ConstVariableValue::Unknown { .. } => None, + ConstVariableValue::Known { value } => Some(value), + } + } + + pub fn is_unknown(&self) -> bool { + match *self { + ConstVariableValue::Unknown { .. } => true, + ConstVariableValue::Known { .. } => false, + } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct ConstVarValue<'tcx> { + pub origin: ConstVariableOrigin, + pub val: ConstVariableValue<'tcx>, +} + +impl<'tcx> UnifyKey for ty::ConstVid<'tcx> { + type Value = ConstVarValue<'tcx>; + fn index(&self) -> u32 { self.index } + fn from_index(i: u32) -> Self { ty::ConstVid { index: i, phantom: PhantomData } } + fn tag() -> &'static str { "ConstVid" } +} + +impl<'tcx> UnifyValue for ConstVarValue<'tcx> { + type Error = (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>); + + fn unify_values(value1: &Self, value2: &Self) -> Result { + let val = match (value1.val, value2.val) { + ( + ConstVariableValue::Known { .. }, + ConstVariableValue::Known { .. } + ) => { + bug!("equating two const variables, both of which have known values") + } + + // If one side is known, prefer that one. + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { + Ok(value1.val) + } + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { + Ok(value2.val) + } + + // If both sides are *unknown*, it hardly matters, does it? + (ConstVariableValue::Unknown { universe: universe1 }, + ConstVariableValue::Unknown { universe: universe2 }) => { + // If we unify two unbound variables, ?T and ?U, then whatever + // value they wind up taking (which must be the same value) must + // be nameable by both universes. Therefore, the resulting + // universe is the minimum of the two universes, because that is + // the one which contains the fewest names in scope. + let universe = cmp::min(universe1, universe2); + Ok(ConstVariableValue::Unknown { universe }) + } + }?; + + Ok(ConstVarValue { + origin: ConstVariableOrigin::ConstInference(DUMMY_SP), + val, + }) + } +} + +impl<'tcx> EqUnifyValue for &'tcx ty::Const<'tcx> {} + +pub fn replace_if_possible( + mut table: RefMut<'_, UnificationTable>>>, + c: &'tcx ty::Const<'tcx> +) -> &'tcx ty::Const<'tcx> { + if let ty::Const { val: ConstValue::Infer(InferConst::Var(vid)), .. } = c { + match table.probe_value(*vid).val.known() { + Some(c) => c, + None => c, + } + } else { + c + } +} diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 18c82ecd38edc..7e45568725f35 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -2,6 +2,7 @@ use std::fmt; use rustc_macros::HashStable; use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef}; +use crate::ty::PlaceholderConst; use crate::hir::def_id::DefId; use super::{EvalResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate}; @@ -26,6 +27,9 @@ pub enum ConstValue<'tcx> { /// Infer the value of the const. Infer(InferConst<'tcx>), + /// A placeholder const - universally quantified higher-ranked const. + Placeholder(PlaceholderConst), + /// Used only for types with `layout::abi::Scalar` ABI and ZSTs. /// /// Not using the enum `Value` to encode that this must not be `Undef`. @@ -58,6 +62,7 @@ impl<'tcx> ConstValue<'tcx> { match *self { ConstValue::Param(_) | ConstValue::Infer(_) | + ConstValue::Placeholder(_) | ConstValue::ByRef(..) | ConstValue::Unevaluated(..) | ConstValue::Slice(..) => None, diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 47d96708ebea3..007ff32f32776 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1248,7 +1248,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let sig = if let ty::Tuple(inputs) = inputs.sty { tcx.mk_fn_sig( inputs.iter().map(|k| k.expect_ty()), - tcx.mk_infer(ty::TyVar(ty::TyVid { index: 0 })), + tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), false, hir::Unsafety::Normal, ::rustc_target::spec::abi::Abi::Rust @@ -1256,7 +1256,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } else { tcx.mk_fn_sig( ::std::iter::once(inputs), - tcx.mk_infer(ty::TyVar(ty::TyVid { index: 0 })), + tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), false, hir::Unsafety::Normal, ::rustc_target::spec::abi::Abi::Rust diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index e725ebb797e37..96212d829d448 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -1,4 +1,4 @@ -use crate::infer::InferCtxt; +use crate::infer::{InferCtxt, ShallowResolver}; use crate::mir::interpret::{GlobalId, ErrorHandled}; use crate::ty::{self, Ty, TypeFoldable, ToPolyTraitRef}; use crate::ty::error::ExpectedFound; @@ -256,8 +256,8 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, if !pending_obligation.stalled_on.is_empty() { if pending_obligation.stalled_on.iter().all(|&ty| { // Use the force-inlined variant of shallow_resolve() because this code is hot. - let resolved_ty = self.selcx.infcx().inlined_shallow_resolve(&ty); - resolved_ty == ty // nothing changed here + let resolved = ShallowResolver::new(self.selcx.infcx()).inlined_shallow_resolve(ty); + resolved == ty // nothing changed here }) { debug!("process_predicate: pending obligation {:?} still stalled on {:?}", self.selcx.infcx() diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 882635e21f57c..dabb8a728901c 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -1371,7 +1371,7 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>( let tcx = selcx.tcx(); let infcx = selcx.infcx(); let closure_sig_ty = vtable.substs.closure_sig_ty(vtable.closure_def_id, tcx); - let closure_sig = infcx.shallow_resolve(&closure_sig_ty).fn_sig(tcx); + let closure_sig = infcx.shallow_resolve(closure_sig_ty).fn_sig(tcx); let Normalized { value: closure_sig, obligations diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 8beabe058cf4f..f0c402789c4cd 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -3124,8 +3124,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // OK to skip binder because the substs on generator types never // touch bound regions, they just capture the in-scope // type/region parameters - let self_ty = self.infcx - .shallow_resolve(obligation.self_ty().skip_binder()); + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); let (generator_def_id, substs) = match self_ty.sty { ty::Generator(id, substs, _) => (id, substs), _ => bug!("closure candidate for non-closure {:?}", obligation), @@ -3182,8 +3181,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // OK to skip binder because the substs on closure types never // touch bound regions, they just capture the in-scope // type/region parameters - let self_ty = self.infcx - .shallow_resolve(obligation.self_ty().skip_binder()); + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); let (closure_def_id, substs) = match self_ty.sty { ty::Closure(id, substs) => (id, substs), _ => bug!("closure candidate for non-closure {:?}", obligation), diff --git a/src/librustc/ty/_match.rs b/src/librustc/ty/_match.rs index 07fa441bb8076..8640216b071ae 100644 --- a/src/librustc/ty/_match.rs +++ b/src/librustc/ty/_match.rs @@ -1,6 +1,7 @@ -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt, InferConst}; use crate::ty::error::TypeError; use crate::ty::relate::{self, Relate, TypeRelation, RelateResult}; +use crate::mir::interpret::ConstValue; /// A type "A" *matches* "B" if the fresh types in B could be /// substituted with values so as to make it equal to A. Matching is @@ -78,6 +79,31 @@ impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Match<'a, 'gcx, 'tcx> { } } + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug!("{}.consts({:?}, {:?})", self.tag(), a, b); + if a == b { + return Ok(a); + } + + match (a.val, b.val) { + (_, ConstValue::Infer(InferConst::Fresh(_))) => { + return Ok(a); + } + + (ConstValue::Infer(_), _) | (_, ConstValue::Infer(_)) => { + return Err(TypeError::ConstMismatch(relate::expected_found(self, &a, &b))); + } + + _ => {} + } + + relate::super_relate_consts(self, a, b) + } + fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) -> RelateResult<'tcx, ty::Binder> where T: Relate<'tcx> diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index b1500456991dd..c07175d9451e6 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -233,6 +233,10 @@ pub struct CommonLifetimes<'tcx> { pub re_erased: Region<'tcx>, } +pub struct CommonConsts<'tcx> { + pub err: &'tcx Const<'tcx>, +} + pub struct LocalTableInContext<'a, V: 'a> { local_id_root: Option, data: &'a ItemLocalMap @@ -980,6 +984,20 @@ impl<'tcx> CommonLifetimes<'tcx> { } } +impl<'tcx> CommonConsts<'tcx> { + fn new(interners: &CtxtInterners<'tcx>, types: &CommonTypes<'tcx>) -> CommonConsts<'tcx> { + let mk_const = |c| { + interners.const_.borrow_mut().intern(c, |c| { + Interned(interners.arena.alloc(c)) + }).0 + }; + + CommonConsts { + err: mk_const(ty::Const::zero_sized(types.err)), + } + } +} + // This struct contains information regarding the `ReFree(FreeRegion)` corresponding to a lifetime // conflict. #[derive(Debug)] @@ -1030,6 +1048,9 @@ pub struct GlobalCtxt<'tcx> { /// Common lifetimes, pre-interned for your convenience. pub lifetimes: CommonLifetimes<'tcx>, + /// Common consts, pre-interned for your convenience. + pub consts: CommonConsts<'tcx>, + /// Map indicating what traits are in scope for places where this /// is relevant; generated by resolve. trait_map: FxHashMap TyCtxt<'a, 'gcx, 'tcx> { let interners = CtxtInterners::new(&arenas.interner); let common_types = CommonTypes::new(&interners); let common_lifetimes = CommonLifetimes::new(&interners); + let common_consts = CommonConsts::new(&interners, &common_types); let dep_graph = hir.dep_graph.clone(); let max_cnum = cstore.crates_untracked().iter().map(|c| c.as_usize()).max().unwrap_or(0); let mut providers = IndexVec::from_elem_n(extern_providers, max_cnum + 1); @@ -1284,6 +1306,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { dep_graph, types: common_types, lifetimes: common_lifetimes, + consts: common_consts, trait_map, export_map: resolutions.export_map.into_iter().map(|(k, v)| { let exports: Vec<_> = v.into_iter().map(|e| { @@ -2650,7 +2673,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { #[inline] pub fn mk_ty_var(self, v: TyVid) -> Ty<'tcx> { - self.mk_infer(TyVar(v)) + self.mk_ty_infer(TyVar(v)) } #[inline] @@ -2663,19 +2686,31 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { #[inline] pub fn mk_int_var(self, v: IntVid) -> Ty<'tcx> { - self.mk_infer(IntVar(v)) + self.mk_ty_infer(IntVar(v)) } #[inline] pub fn mk_float_var(self, v: FloatVid) -> Ty<'tcx> { - self.mk_infer(FloatVar(v)) + self.mk_ty_infer(FloatVar(v)) } #[inline] - pub fn mk_infer(self, it: InferTy) -> Ty<'tcx> { + pub fn mk_ty_infer(self, it: InferTy) -> Ty<'tcx> { self.mk_ty(Infer(it)) } + #[inline] + pub fn mk_const_infer( + self, + ic: InferConst<'tcx>, + ty: Ty<'tcx>, + ) -> &'tcx ty::Const<'tcx> { + self.mk_const(ty::Const { + val: ConstValue::Infer(ic), + ty, + }) + } + #[inline] pub fn mk_ty_param(self, index: u32, diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 74d0a29bcff00..4e4024d5bab43 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -44,6 +44,8 @@ pub enum TypeError<'tcx> { ProjectionMismatched(ExpectedFound), ProjectionBoundsLength(ExpectedFound), ExistentialMismatch(ExpectedFound<&'tcx ty::List>>), + + ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), } #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] @@ -163,6 +165,9 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { report_maybe_different(f, &format!("trait `{}`", values.expected), &format!("trait `{}`", values.found)) } + ConstMismatch(ref values) => { + write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) + } } } } diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index cb4724adc932c..8d7e7e16e85cb 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -254,6 +254,9 @@ impl FlagComputation { ConstValue::Param(_) => { self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES | TypeFlags::HAS_PARAMS); } + ConstValue::Placeholder(_) => { + self.add_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_CT_PLACEHOLDER); + } _ => {}, } } diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 7f58a436fcf0d..dbf9047f775bf 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -32,6 +32,7 @@ //! looking for, and does not need to visit anything else. use crate::hir::def_id::DefId; +use crate::mir::interpret::ConstValue; use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags, flags::FlagComputation}; use std::collections::BTreeMap; @@ -96,7 +97,11 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { ) } fn has_placeholders(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_PLACEHOLDER | TypeFlags::HAS_TY_PLACEHOLDER) + self.has_type_flags( + TypeFlags::HAS_RE_PLACEHOLDER | + TypeFlags::HAS_TY_PLACEHOLDER | + TypeFlags::HAS_CT_PLACEHOLDER + ) } fn needs_subst(&self) -> bool { self.has_type_flags(TypeFlags::NEEDS_SUBST) @@ -193,29 +198,37 @@ pub trait TypeVisitor<'tcx> : Sized { /////////////////////////////////////////////////////////////////////////// // Some sample folders -pub struct BottomUpFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a, F, G> +pub struct BottomUpFolder<'a, 'gcx: 'a+'tcx, 'tcx: 'a, F, G, H> where F: FnMut(Ty<'tcx>) -> Ty<'tcx>, G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>, + H: FnMut(&'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx>, { pub tcx: TyCtxt<'a, 'gcx, 'tcx>, - pub fldop: F, - pub reg_op: G, + pub ty_op: F, + pub lt_op: G, + pub ct_op: H, } -impl<'a, 'gcx, 'tcx, F, G> TypeFolder<'gcx, 'tcx> for BottomUpFolder<'a, 'gcx, 'tcx, F, G> +impl<'a, 'gcx, 'tcx, F, G, H> TypeFolder<'gcx, 'tcx> for BottomUpFolder<'a, 'gcx, 'tcx, F, G, H> where F: FnMut(Ty<'tcx>) -> Ty<'tcx>, G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>, + H: FnMut(&'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx>, { fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - let t1 = ty.super_fold_with(self); - (self.fldop)(t1) + let t = ty.super_fold_with(self); + (self.ty_op)(t) } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { let r = r.super_fold_with(self); - (self.reg_op)(r) + (self.lt_op)(r) + } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + let ct = ct.super_fold_with(self); + (self.ct_op)(ct) } } @@ -422,22 +435,26 @@ struct BoundVarReplacer<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { fld_r: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), fld_t: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a), + fld_c: &'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx> + 'a), } impl<'a, 'gcx, 'tcx> BoundVarReplacer<'a, 'gcx, 'tcx> { - fn new( + fn new( tcx: TyCtxt<'a, 'gcx, 'tcx>, fld_r: &'a mut F, - fld_t: &'a mut G + fld_t: &'a mut G, + fld_c: &'a mut H, ) -> Self where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, - G: FnMut(ty::BoundTy) -> Ty<'tcx> + G: FnMut(ty::BoundTy) -> Ty<'tcx>, + H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>, { BoundVarReplacer { tcx, current_index: ty::INNERMOST, fld_r, fld_t, + fld_c, } } } @@ -497,6 +514,32 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for BoundVarReplacer<'a, 'gcx, 'tcx> _ => r } } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if let ty::Const { + val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, bound_const)), + ty, + } = *ct { + if debruijn == self.current_index { + let fld_c = &mut self.fld_c; + let ct = fld_c(bound_const, ty); + ty::fold::shift_vars( + self.tcx, + &ct, + self.current_index.as_u32() + ) + } else { + ct + } + } else { + if !ct.has_vars_bound_at_or_above(self.current_index) { + // Nothing more to substitute. + ct + } else { + ct.super_fold_with(self) + } + } + } } impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { @@ -519,27 +562,34 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, T: TypeFoldable<'tcx> { - // identity for bound types + // identity for bound types and consts let fld_t = |bound_ty| self.mk_ty(ty::Bound(ty::INNERMOST, bound_ty)); - self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t) + let fld_c = |bound_ct, ty| { + self.mk_const_infer(ty::InferConst::Canonical(ty::INNERMOST, bound_ct), ty) + }; + self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t, fld_c) } /// Replaces all escaping bound vars. The `fld_r` closure replaces escaping - /// bound regions while the `fld_t` closure replaces escaping bound types. - pub fn replace_escaping_bound_vars( + /// bound regions; the `fld_t` closure replaces escaping bound types and the `fld_c` + /// closure replaces escaping bound consts. + pub fn replace_escaping_bound_vars( self, value: &T, mut fld_r: F, - mut fld_t: G + mut fld_t: G, + mut fld_c: H, ) -> (T, BTreeMap>) where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, G: FnMut(ty::BoundTy) -> Ty<'tcx>, - T: TypeFoldable<'tcx> + H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>, + T: TypeFoldable<'tcx>, { use rustc_data_structures::fx::FxHashMap; let mut region_map = BTreeMap::new(); let mut type_map = FxHashMap::default(); + let mut const_map = FxHashMap::default(); if !value.has_escaping_bound_vars() { (value.clone(), region_map) @@ -552,7 +602,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { *type_map.entry(bound_ty).or_insert_with(|| fld_t(bound_ty)) }; - let mut replacer = BoundVarReplacer::new(self, &mut real_fld_r, &mut real_fld_t); + let mut real_fld_c = |bound_ct, ty| { + *const_map.entry(bound_ct).or_insert_with(|| fld_c(bound_ct, ty)) + }; + + let mut replacer = BoundVarReplacer::new( + self, + &mut real_fld_r, + &mut real_fld_t, + &mut real_fld_c, + ); let result = value.fold_with(&mut replacer); (result, region_map) } @@ -561,17 +620,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Replaces all types or regions bound by the given `Binder`. The `fld_r` /// closure replaces bound regions while the `fld_t` closure replaces bound /// types. - pub fn replace_bound_vars( + pub fn replace_bound_vars( self, value: &Binder, fld_r: F, - fld_t: G + fld_t: G, + fld_c: H, ) -> (T, BTreeMap>) where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, G: FnMut(ty::BoundTy) -> Ty<'tcx>, + H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>, T: TypeFoldable<'tcx> { - self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t) + self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t, fld_c) } /// Replaces any late-bound regions bound in `value` with @@ -732,6 +793,28 @@ impl TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> { _ => ty.super_fold_with(self), } } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if let ty::Const { + val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, bound_const)), + ty, + } = *ct { + if self.amount == 0 || debruijn < self.current_index { + ct + } else { + let debruijn = match self.direction { + Direction::In => debruijn.shifted_in(self.amount), + Direction::Out => { + assert!(debruijn.as_u32() >= self.amount); + debruijn.shifted_out(self.amount) + } + }; + self.tcx.mk_const_infer(ty::InferConst::Canonical(debruijn, bound_const), ty) + } + } else { + ct.super_fold_with(self) + } + } } pub fn shift_region<'a, 'gcx, 'tcx>( @@ -824,6 +907,17 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { // visited. r.bound_at_or_above_binder(self.outer_index) } + + fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> bool { + if let ty::Const { + val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, _)), + .. + } = *ct { + debruijn >= self.outer_index + } else { + false + } + } } struct HasTypeFlagsVisitor { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 6b938ea2fccdc..36da21b71f536 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -96,6 +96,7 @@ mod constness; pub mod error; mod erase_regions; pub mod fast_reject; +pub mod flags; pub mod fold; pub mod inhabitedness; pub mod layout; @@ -112,7 +113,6 @@ pub mod wf; pub mod util; mod context; -mod flags; mod instance; mod structural_impls; mod sty; @@ -455,6 +455,7 @@ bitflags! { const HAS_TY_PLACEHOLDER = 1 << 14; const HAS_CT_INFER = 1 << 15; + const HAS_CT_PLACEHOLDER = 1 << 16; const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits | TypeFlags::HAS_SELF.bits | @@ -477,7 +478,8 @@ bitflags! { TypeFlags::HAS_FREE_LOCAL_NAMES.bits | TypeFlags::KEEP_IN_LOCAL_TCX.bits | TypeFlags::HAS_RE_LATE_BOUND.bits | - TypeFlags::HAS_TY_PLACEHOLDER.bits; + TypeFlags::HAS_TY_PLACEHOLDER.bits | + TypeFlags::HAS_CT_PLACEHOLDER.bits; } } @@ -1630,6 +1632,8 @@ pub type PlaceholderRegion = Placeholder; pub type PlaceholderType = Placeholder; +pub type PlaceholderConst = Placeholder; + /// When type checking, we use the `ParamEnv` to track /// details about the set of where-clauses that are in scope at this /// particular point. diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs index e5803b7be4f63..d2cc251453610 100644 --- a/src/librustc/ty/print/pretty.rs +++ b/src/librustc/ty/print/pretty.rs @@ -712,7 +712,7 @@ pub trait PrettyPrinter<'gcx: 'tcx, 'tcx>: // in order to place the projections inside the `<...>`. if !resugared { // Use a type that can't appear in defaults of type parameters. - let dummy_self = self.tcx().mk_infer(ty::FreshTy(0)); + let dummy_self = self.tcx().mk_ty_infer(ty::FreshTy(0)); let principal = principal.with_self_ty(self.tcx(), dummy_self); let args = self.generic_args_to_print( @@ -1481,7 +1481,7 @@ define_print_and_forward_display! { ty::ExistentialTraitRef<'tcx> { // Use a type that can't appear in defaults of type parameters. - let dummy_self = cx.tcx().mk_infer(ty::FreshTy(0)); + let dummy_self = cx.tcx().mk_ty_infer(ty::FreshTy(0)); let trait_ref = self.with_self_ty(cx.tcx(), dummy_self); p!(print(trait_ref)) } diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 2638a1c7c8808..d7b1907074195 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -8,7 +8,7 @@ use crate::hir::def_id::DefId; use crate::ty::subst::{Kind, UnpackedKind, SubstsRef}; use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; use crate::ty::error::{ExpectedFound, TypeError}; -use crate::mir::interpret::{GlobalId, ConstValue}; +use crate::mir::interpret::{GlobalId, ConstValue, Scalar}; use crate::util::common::ErrorReported; use syntax_pos::DUMMY_SP; use std::rc::Rc; @@ -76,11 +76,19 @@ pub trait TypeRelation<'a, 'gcx: 'a+'tcx, 'tcx: 'a> : Sized { // additional hooks for other types in the future if needed // without making older code, which called `relate`, obsolete. - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) - -> RelateResult<'tcx, Ty<'tcx>>; + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>; - fn regions(&mut self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) - -> RelateResult<'tcx, ty::Region<'tcx>>; + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx> + ) -> RelateResult<'tcx, ty::Region<'tcx>>; + + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx> + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>; fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) -> RelateResult<'tcx, ty::Binder> @@ -116,7 +124,7 @@ impl<'tcx> Relate<'tcx> for ty::TypeAndMut<'tcx> { ast::Mutability::MutMutable => ty::Invariant, }; let ty = relation.relate_with_variance(variance, &a.ty, &b.ty)?; - Ok(ty::TypeAndMut {ty: ty, mutbl: mutbl}) + Ok(ty::TypeAndMut { ty, mutbl }) } } } @@ -468,6 +476,8 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, let t = relation.relate(&a_t, &b_t)?; let to_u64 = |x: ty::Const<'tcx>| -> Result { match x.val { + // FIXME(const_generics): this doesn't work right now, + // because it tries to relate an `Infer` to a `Param`. ConstValue::Unevaluated(def_id, substs) => { // FIXME(eddyb) get the right param_env. let param_env = ty::ParamEnv::empty(); @@ -481,7 +491,7 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, if let Some(instance) = instance { let cid = GlobalId { instance, - promoted: None + promoted: None, }; if let Some(s) = tcx.const_eval(param_env.and(cid)) .ok() @@ -575,6 +585,62 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, } } +/// The main "const relation" routine. Note that this does not handle +/// inference artifacts, so you should filter those out before calling +/// it. +pub fn super_relate_consts<'a, 'gcx, 'tcx, R>( + relation: &mut R, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx> +) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> +where + R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'a+'tcx, 'tcx: 'a +{ + let tcx = relation.tcx(); + + // Currently, the values that can be unified are those that + // implement both `PartialEq` and `Eq`, corresponding to + // `structural_match` types. + // FIXME(const_generics): check for `structural_match` synthetic attribute. + match (a.val, b.val) { + (ConstValue::Infer(_), _) | (_, ConstValue::Infer(_)) => { + // The caller should handle these cases! + bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b) + } + (ConstValue::Param(a_p), ConstValue::Param(b_p)) if a_p.index == b_p.index => { + Ok(a) + } + (ConstValue::Placeholder(p1), ConstValue::Placeholder(p2)) if p1 == p2 => { + Ok(a) + } + (ConstValue::Scalar(Scalar::Bits { .. }), _) if a == b => { + Ok(a) + } + (ConstValue::ByRef(..), _) => { + bug!( + "non-Scalar ConstValue encountered in super_relate_consts {:?} {:?}", + a, + b, + ); + } + + // FIXME(const_generics): this is wrong, as it is a projection + (ConstValue::Unevaluated(a_def_id, a_substs), + ConstValue::Unevaluated(b_def_id, b_substs)) if a_def_id == b_def_id => { + let substs = + relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?; + Ok(tcx.mk_const(ty::Const { + val: ConstValue::Unevaluated(a_def_id, &substs), + ty: a.ty, + })) + } + + _ => { + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } + } +} + impl<'tcx> Relate<'tcx> for &'tcx ty::List> { fn relate<'a, 'gcx, R>(relation: &mut R, a: &Self, @@ -646,6 +712,17 @@ impl<'tcx> Relate<'tcx> for ty::Region<'tcx> { } } +impl<'tcx> Relate<'tcx> for &'tcx ty::Const<'tcx> { + fn relate<'a, 'gcx, R>(relation: &mut R, + a: &&'tcx ty::Const<'tcx>, + b: &&'tcx ty::Const<'tcx>) + -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> + where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'a+'tcx, 'tcx: 'a + { + relation.consts(*a, *b) + } +} + impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder { fn relate<'a, 'gcx, R>(relation: &mut R, a: &ty::Binder, @@ -699,14 +776,17 @@ impl<'tcx> Relate<'tcx> for Kind<'tcx> { (UnpackedKind::Type(a_ty), UnpackedKind::Type(b_ty)) => { Ok(relation.relate(&a_ty, &b_ty)?.into()) } + (UnpackedKind::Const(a_ct), UnpackedKind::Const(b_ct)) => { + Ok(relation.relate(&a_ct, &b_ct)?.into()) + } (UnpackedKind::Lifetime(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } (UnpackedKind::Type(unpacked), x) => { bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } - (UnpackedKind::Const(_), _) => { - unimplemented!() // FIXME(const_generics) + (UnpackedKind::Const(unpacked), x) => { + bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) } } } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 9aaff33e93339..504b939c5f2d6 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -737,7 +737,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { ProjectionMismatched(x) => ProjectionMismatched(x), ProjectionBoundsLength(x) => ProjectionBoundsLength(x), Sorts(ref x) => return tcx.lift(x).map(Sorts), - ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch) + ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), + ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch), }) } } @@ -1320,6 +1321,7 @@ EnumTypeFoldableImpl! { (ty::error::TypeError::ProjectionBoundsLength)(x), (ty::error::TypeError::Sorts)(x), (ty::error::TypeError::ExistentialMismatch)(x), + (ty::error::TypeError::ConstMismatch)(x), } } @@ -1353,6 +1355,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> { // FIXME(const_generics): implement TypeFoldable for InferConst ConstValue::Infer(ic) => ConstValue::Infer(ic), ConstValue::Param(p) => ConstValue::Param(p.fold_with(folder)), + ConstValue::Placeholder(p) => ConstValue::Placeholder(p), ConstValue::Scalar(a) => ConstValue::Scalar(a), ConstValue::Slice(a, b) => ConstValue::Slice(a, b), ConstValue::Unevaluated(did, substs) @@ -1364,8 +1367,9 @@ impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> { match *self { ConstValue::ByRef(..) => false, // FIXME(const_generics): implement TypeFoldable for InferConst - ConstValue::Infer(_ic) => false, + ConstValue::Infer(_) => false, ConstValue::Param(p) => p.visit_with(visitor), + ConstValue::Placeholder(_) => false, ConstValue::Scalar(_) => false, ConstValue::Slice(..) => false, ConstValue::Unevaluated(_, substs) => substs.visit_with(visitor), diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index b793b37fb2ae1..c474baac3d129 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -516,7 +516,7 @@ pub fn object_region_bounds<'a, 'gcx, 'tcx>( // Since we don't actually *know* the self type for an object, // this "open(err)" serves as a kind of dummy standin -- basically // a placeholder type. - let open_ty = tcx.mk_infer(ty::FreshTy(0)); + let open_ty = tcx.mk_ty_infer(ty::FreshTy(0)); let predicates = existential_predicates.iter().filter_map(|predicate| { if let ty::ExistentialPredicate::Projection(_) = *predicate.skip_binder() { diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs index c2b1021f816a6..3b8e5b4495383 100644 --- a/src/librustc_codegen_ssa/mir/operand.rs +++ b/src/librustc_codegen_ssa/mir/operand.rs @@ -79,6 +79,7 @@ impl<'a, 'tcx: 'a, V: CodegenObject> OperandRef<'tcx, V> { ConstValue::Unevaluated(..) => bug!("unevaluated constant in `OperandRef::from_const`"), ConstValue::Param(_) => bug!("encountered a ConstValue::Param in codegen"), ConstValue::Infer(_) => bug!("encountered a ConstValue::Infer in codegen"), + ConstValue::Placeholder(_) => bug!("encountered a ConstValue::Placeholder in codegen"), ConstValue::Scalar(x) => { let scalar = match layout.abi { layout::Abi::Scalar(ref x) => x, diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 55c1bfb17dec3..ed7c316e3da75 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -524,7 +524,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> layout: Option>, ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { let op = match val.val { - ConstValue::Param(_) | ConstValue::Infer(_) => bug!(), + ConstValue::Param(_) => return err!(TooGeneric), + ConstValue::Infer(_) | ConstValue::Placeholder(_) => bug!(), ConstValue::ByRef(ptr, alloc) => { // We rely on mutability being set correctly in that allocation to prevent writes // where none should happen -- and for `static mut`, we copy on demand anyway. diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index 2fc0e08834a6e..b001a09529e5b 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -397,7 +397,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { // FIXME(const_generics): handle debug printing. pub fn push_const_name(&self, c: &Const<'tcx>, output: &mut String, debug: bool) { match c.val { - ConstValue::Infer(..) => output.push_str("_"), + ConstValue::Infer(..) | ConstValue::Placeholder(_) => output.push_str("_"), ConstValue::Param(ParamConst { name, .. }) => { write!(output, "{}", name).unwrap(); } diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs index 4f5a4996db537..9d234e93b837b 100644 --- a/src/librustc_traits/chalk_context/resolvent_ops.rs +++ b/src/librustc_traits/chalk_context/resolvent_ops.rs @@ -16,9 +16,10 @@ use rustc::traits::{ Environment, InEnvironment, }; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, InferConst}; use rustc::ty::subst::Kind; use rustc::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc::mir::interpret::ConstValue; use syntax_pos::DUMMY_SP; use super::{ChalkInferenceContext, ChalkArenas, ChalkExClause, ConstrainedSubst}; @@ -275,4 +276,44 @@ impl TypeRelation<'cx, 'gcx, 'tcx> for AnswerSubstitutor<'cx, 'gcx, 'tcx> { Ok(a) } + + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + if let ty::Const { + val: ConstValue::Infer(InferConst::Canonical(debruijn, bound_ct)), + .. + } = a { + if *debruijn == self.binder_index { + self.unify_free_answer_var(*bound_ct, b.into())?; + return Ok(b); + } + } + + match (a, b) { + ( + ty::Const { + val: ConstValue::Infer(InferConst::Canonical(a_debruijn, a_bound)), + .. + }, + ty::Const { + val: ConstValue::Infer(InferConst::Canonical(b_debruijn, b_bound)), + .. + }, + ) => { + assert_eq!(a_debruijn, b_debruijn); + assert_eq!(a_bound, b_bound); + Ok(a) + } + + // Everything else should just be a perfect match as well, + // and we forbid inference variables. + _ => match ty::relate::super_relate_consts(self, a, b) { + Ok(ct) => Ok(ct), + Err(err) => bug!("const mismatch in `AnswerSubstitutor`: {}", err), + } + } + } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 5a420c3661594..a35777873abdf 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -21,6 +21,7 @@ use rustc::traits::query::method_autoderef::{MethodAutoderefBadTy}; use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable}; use rustc::ty::GenericParamDefKind; use rustc::infer::type_variable::TypeVariableOrigin; +use rustc::infer::unify_key::ConstVariableOrigin; use rustc::util::nodemap::FxHashSet; use rustc::infer::{self, InferOk}; use rustc::infer::canonical::{Canonical, QueryResponse}; @@ -1572,7 +1573,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.tcx.def_span(def_id))).into() } GenericParamDefKind::Const { .. } => { - unimplemented!() // FIXME(const_generics) + let span = self.tcx.def_span(def_id); + let origin = ConstVariableOrigin::SubstitutionPlaceholder(span); + self.next_const_var(self.tcx.type_of(param.def_id), origin).into() } } }) diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 37be1c3b5b7fa..8ee30c0d2d31d 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -616,7 +616,7 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>( let mut substituted_predicates = Vec::new(); ty.fold_with(&mut ty::fold::BottomUpFolder { tcx: fcx.tcx, - fldop: |ty| { + ty_op: |ty| { if let ty::Opaque(def_id, substs) = ty.sty { trace!("check_existential_types: opaque_ty, {:?}, {:?}", def_id, substs); let generics = tcx.generics_of(def_id); @@ -739,7 +739,8 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>( } // if let Opaque ty }, - reg_op: |reg| reg, + lt_op: |lt| lt, + ct_op: |ct| ct, }); substituted_predicates } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 80da3fd975124..f9d83146e30c3 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -12,6 +12,7 @@ use rustc::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder}; use rustc::ty::subst::UnpackedKind; use rustc::ty::{self, Ty, TyCtxt}; +use rustc::mir::interpret::ConstValue; use rustc::util::nodemap::DefIdSet; use rustc_data_structures::sync::Lrc; use std::mem; @@ -488,7 +489,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { // figures out the concrete type with `U`, but the stored type is with `T` instantiated_ty.fold_with(&mut BottomUpFolder { tcx: self.tcx().global_tcx(), - fldop: |ty| { + ty_op: |ty| { trace!("checking type {:?}", ty); // find a type parameter if let ty::Param(..) = ty.sty { @@ -520,7 +521,7 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } ty }, - reg_op: |region| { + lt_op: |region| { match region { // ignore static regions ty::ReStatic => region, @@ -564,6 +565,39 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } } }, + ct_op: |ct| { + trace!("checking const {:?}", ct); + // Find a const parameter + if let ConstValue::Param(..) = ct.val { + // look it up in the substitution list + assert_eq!(opaque_defn.substs.len(), generics.params.len()); + for (subst, param) in opaque_defn.substs.iter() + .zip(&generics.params) { + if let UnpackedKind::Const(subst) = subst.unpack() { + if subst == ct { + // found it in the substitution list, replace with the + // parameter from the existential type + return self.tcx() + .global_tcx() + .mk_const_param(param.index, param.name, ct.ty); + } + } + } + self.tcx() + .sess + .struct_span_err( + span, + &format!( + "const parameter `{}` is part of concrete type but not \ + used in parameter list for existential type", + ct, + ), + ) + .emit(); + return self.tcx().consts.err; + } + ct + } }) }; @@ -819,6 +853,21 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Resolver<'cx, 'gcx, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { self.infcx.fully_resolve(&r).unwrap_or(self.tcx.lifetimes.re_static) } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + match self.infcx.fully_resolve(&ct) { + Ok(ct) => ct, + Err(_) => { + debug!( + "Resolver::fold_const: input const `{:?}` not fully resolvable", + ct + ); + // FIXME: we'd like to use `self.report_error`, but it doesn't yet + // accept a &'tcx ty::Const. + self.tcx().consts.err + } + } + } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 894a1c82e876f..9c8b5ac58e337 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -15,7 +15,7 @@ //! crate as a kind of pass. This should eventually be factored away. use crate::astconv::{AstConv, Bounds}; -use crate::constrained_generic_params as ctp; +use crate::constrained_generic_params as cgp; use crate::check::intrinsic::intrisic_operation_unsafety; use crate::lint; use crate::middle::lang_items::SizedTraitLangItem; @@ -2202,11 +2202,11 @@ fn explicit_predicates_of<'a, 'tcx>( { let self_ty = tcx.type_of(def_id); let trait_ref = tcx.impl_trait_ref(def_id); - ctp::setup_constraining_predicates( + cgp::setup_constraining_predicates( tcx, &mut predicates, trait_ref, - &mut ctp::parameters_for_impl(self_ty, trait_ref), + &mut cgp::parameters_for_impl(self_ty, trait_ref), ); } diff --git a/src/librustc_typeck/impl_wf_check.rs b/src/librustc_typeck/impl_wf_check.rs index 2b72f43d36f72..e7ec5bc81c769 100644 --- a/src/librustc_typeck/impl_wf_check.rs +++ b/src/librustc_typeck/impl_wf_check.rs @@ -8,7 +8,7 @@ //! specialization errors. These things can (and probably should) be //! fixed, but for the moment it's easier to do these checks early. -use crate::constrained_generic_params as ctp; +use crate::constrained_generic_params as cgp; use rustc::hir; use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::def_id::DefId; @@ -102,8 +102,8 @@ fn enforce_impl_params_are_constrained<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let impl_predicates = tcx.predicates_of(impl_def_id); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); - let mut input_parameters = ctp::parameters_for_impl(impl_self_ty, impl_trait_ref); - ctp::identify_constrained_generic_params( + let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref); + cgp::identify_constrained_generic_params( tcx, &impl_predicates, impl_trait_ref, &mut input_parameters); // Disallow unconstrained lifetimes, but only if they appear in assoc types. @@ -114,7 +114,7 @@ fn enforce_impl_params_are_constrained<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item.kind == ty::AssociatedKind::Type && item.defaultness.has_value() }) .flat_map(|def_id| { - ctp::parameters_for(&tcx.type_of(def_id), true) + cgp::parameters_for(&tcx.type_of(def_id), true) }).collect(); for param in &impl_generics.params { @@ -122,7 +122,7 @@ fn enforce_impl_params_are_constrained<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Disallow ANY unconstrained type parameters. ty::GenericParamDefKind::Type { .. } => { let param_ty = ty::ParamTy::for_def(param); - if !input_parameters.contains(&ctp::Parameter::from(param_ty)) { + if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", @@ -130,7 +130,7 @@ fn enforce_impl_params_are_constrained<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } ty::GenericParamDefKind::Lifetime => { - let param_lt = ctp::Parameter::from(param.to_early_bound_region_data()); + let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); if lifetimes_in_associated_types.contains(¶m_lt) && // (*) !input_parameters.contains(¶m_lt) { report_unused_parameter(tcx, @@ -141,7 +141,7 @@ fn enforce_impl_params_are_constrained<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } ty::GenericParamDefKind::Const => { let param_ct = ty::ParamConst::for_def(param); - if !input_parameters.contains(&ctp::Parameter::from(param_ct)) { + if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { report_unused_parameter(tcx, tcx.def_span(param.def_id), "const", diff --git a/src/test/ui/const-generics/cannot-infer-const-args.rs b/src/test/ui/const-generics/cannot-infer-const-args.rs new file mode 100644 index 0000000000000..e1061c6d1a33d --- /dev/null +++ b/src/test/ui/const-generics/cannot-infer-const-args.rs @@ -0,0 +1,10 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn foo() -> usize { + 0 +} + +fn main() { + foo(); //~ ERROR type annotations needed +} diff --git a/src/test/ui/const-generics/cannot-infer-const-args.stderr b/src/test/ui/const-generics/cannot-infer-const-args.stderr new file mode 100644 index 0000000000000..5528c2fca6a3b --- /dev/null +++ b/src/test/ui/const-generics/cannot-infer-const-args.stderr @@ -0,0 +1,15 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/cannot-infer-const-args.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + +error[E0282]: type annotations needed + --> $DIR/cannot-infer-const-args.rs:9:5 + | +LL | foo(); + | ^^^ cannot infer type for `fn() -> usize {foo::<_>}` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/const-arg-in-fn.rs b/src/test/ui/const-generics/const-arg-in-fn.rs new file mode 100644 index 0000000000000..3f86782838ca1 --- /dev/null +++ b/src/test/ui/const-generics/const-arg-in-fn.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn const_u32_identity() -> u32 { + X +} + + fn main() { + let val = const_u32_identity::<18>(); + assert_eq!(val, 18); +} diff --git a/src/test/ui/const-generics/const-arg-in-fn.stderr b/src/test/ui/const-generics/const-arg-in-fn.stderr new file mode 100644 index 0000000000000..e32b714b25d36 --- /dev/null +++ b/src/test/ui/const-generics/const-arg-in-fn.stderr @@ -0,0 +1,6 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/const-arg-in-fn.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + diff --git a/src/test/ui/const-generics/const-types.rs b/src/test/ui/const-generics/const-types.rs new file mode 100644 index 0000000000000..11757cd588dab --- /dev/null +++ b/src/test/ui/const-generics/const-types.rs @@ -0,0 +1,16 @@ +// run-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +#[allow(dead_code)] + +struct ConstArray { + array: [T; LEN], +} + +fn main() { + let arr = ConstArray:: { + array: [0; 8], + }; +} diff --git a/src/test/ui/const-generics/const-types.stderr b/src/test/ui/const-generics/const-types.stderr new file mode 100644 index 0000000000000..fbf5d53754164 --- /dev/null +++ b/src/test/ui/const-generics/const-types.stderr @@ -0,0 +1,6 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/const-types.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.rs b/src/test/ui/const-generics/incorrect-number-of-const-args.rs new file mode 100644 index 0000000000000..7059e9d8348e3 --- /dev/null +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.rs @@ -0,0 +1,11 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn foo() -> usize { + 0 +} + +fn main() { + foo::<0>(); //~ ERROR wrong number of const arguments: expected 2, found 1 + foo::<0, 0, 0>(); //~ ERROR wrong number of const arguments: expected 2, found 3 +} diff --git a/src/test/ui/const-generics/incorrect-number-of-const-args.stderr b/src/test/ui/const-generics/incorrect-number-of-const-args.stderr new file mode 100644 index 0000000000000..11727733eb533 --- /dev/null +++ b/src/test/ui/const-generics/incorrect-number-of-const-args.stderr @@ -0,0 +1,21 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/incorrect-number-of-const-args.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + +error[E0107]: wrong number of const arguments: expected 2, found 1 + --> $DIR/incorrect-number-of-const-args.rs:9:5 + | +LL | foo::<0>(); + | ^^^^^^^^ expected 2 const arguments + +error[E0107]: wrong number of const arguments: expected 2, found 3 + --> $DIR/incorrect-number-of-const-args.rs:10:17 + | +LL | foo::<0, 0, 0>(); + | ^ unexpected const argument + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0107`.