Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type inference for inline consts #89561

Merged
merged 8 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Implement type inference for inline consts
In most cases it is handled in the same way as closures.
  • Loading branch information
nbdd0121 committed Nov 7, 2021
commit 468192a9c52e613f56af5b2a967d33c326cbf373
3 changes: 2 additions & 1 deletion compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 569,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// to store those. Otherwise, we'll pass in `None` to the
// functions below, which will trigger them to report errors
// eagerly.
let mut outlives_requirements = infcx.tcx.is_closure(mir_def_id).then(Vec::new);
let mut outlives_requirements =
infcx.tcx.is_closure_or_inline_const(mir_def_id).then(Vec::new);
nbdd0121 marked this conversation as resolved.
Show resolved Hide resolved

self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer);

Expand Down
14 changes: 12 additions & 2 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1345,7 1345,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Some(RETURN_PLACE) => {
if let BorrowCheckContext {
universal_regions:
UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. },
UniversalRegions {
defining_ty:
DefiningTy::Const(def_id, _)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we extract this into a method, so that we write

defining_ty.is_any_const()

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is is_const that does this, but this one needs to match def_id. I'll split it into a is_const call followed by a def_id call.

| DefiningTy::InlineConst(def_id, _),
..
},
..
} = self.borrowck_context
{
Expand Down Expand Up @@ -1650,7 1655,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Some(RETURN_PLACE) => {
if let BorrowCheckContext {
universal_regions:
UniversalRegions { defining_ty: DefiningTy::Const(def_id, _), .. },
UniversalRegions {
defining_ty:
DefiningTy::Const(def_id, _)
| DefiningTy::InlineConst(def_id, _),
..
},
..
} = self.borrowck_context
{
Expand Down
49 changes: 39 additions & 10 deletions compiler/rustc_borrowck/src/universal_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 23,7 @@ use rustc_index::vec::{Idx, IndexVec};
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
use rustc_middle::ty::{self, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt};
use std::iter;

use crate::nll::ToRegionVid;
Expand Down Expand Up @@ -108,6 108,10 @@ pub enum DefiningTy<'tcx> {
/// is that it has no inputs and a single return value, which is
/// the value of the constant.
Const(DefId, SubstsRef<'tcx>),

/// The MIR represents an inline const. The signature has no inputs and a
/// single return value found via `InlineConstSubsts::ty`.
InlineConst(DefId, SubstsRef<'tcx>),
}

impl<'tcx> DefiningTy<'tcx> {
Expand All @@ -121,7 125,7 @@ impl<'tcx> DefiningTy<'tcx> {
DefiningTy::Generator(_, substs, _) => {
Either::Right(Either::Left(substs.as_generator().upvar_tys()))
}
DefiningTy::FnDef(..) | DefiningTy::Const(..) => {
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => {
Either::Right(Either::Right(iter::empty()))
}
}
Expand All @@ -133,7 137,7 @@ impl<'tcx> DefiningTy<'tcx> {
pub fn implicit_inputs(self) -> usize {
match self {
DefiningTy::Closure(..) | DefiningTy::Generator(..) => 1,
DefiningTy::FnDef(..) | DefiningTy::Const(..) => 0,
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => 0,
}
}

Expand All @@ -142,15 146,16 @@ impl<'tcx> DefiningTy<'tcx> {
}

pub fn is_const(&self) -> bool {
matches!(*self, DefiningTy::Const(..))
matches!(*self, DefiningTy::Const(..) | DefiningTy::InlineConst(..))
}

pub fn def_id(&self) -> DefId {
match *self {
DefiningTy::Closure(def_id, ..)
| DefiningTy::Generator(def_id, ..)
| DefiningTy::FnDef(def_id, ..)
| DefiningTy::Const(def_id, ..) => def_id,
| DefiningTy::Const(def_id, ..)
| DefiningTy::InlineConst(def_id, ..) => def_id,
}
}
}
Expand Down Expand Up @@ -376,6 381,12 @@ impl<'tcx> UniversalRegions<'tcx> {
tcx.def_path_str_with_substs(def_id, substs),
));
}
DefiningTy::InlineConst(def_id, substs) => {
err.note(&format!(
"defining inline constant type: {}",
tcx.def_path_str_with_substs(def_id, substs),
));
}
}
}
}
Expand Down Expand Up @@ -534,11 545,21 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
}

BodyOwnerKind::Const | BodyOwnerKind::Static(..) => {
assert_eq!(self.mir_def.did.to_def_id(), closure_base_def_id);
let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id);
let substs =
self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs);
DefiningTy::Const(self.mir_def.did.to_def_id(), substs)
if self.mir_def.did.to_def_id() == closure_base_def_id {
nbdd0121 marked this conversation as resolved.
Show resolved Hide resolved
let substs =
self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs);
DefiningTy::Const(self.mir_def.did.to_def_id(), substs)
} else {
let ty = tcx.typeck(self.mir_def.did).node_type(self.mir_hir_id);
let substs = InlineConstSubsts::new(
tcx,
InlineConstSubstsParts { parent_substs: identity_substs, ty },
)
.substs;
let substs = self.infcx.replace_free_regions_with_nll_infer_vars(FR, substs);
DefiningTy::InlineConst(self.mir_def.did.to_def_id(), substs)
}
}
}
}
Expand All @@ -556,7 577,9 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
let closure_base_def_id = tcx.closure_base_def_id(self.mir_def.did.to_def_id());
let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id);
let fr_substs = match defining_ty {
DefiningTy::Closure(_, ref substs) | DefiningTy::Generator(_, ref substs, _) => {
DefiningTy::Closure(_, ref substs)
| DefiningTy::Generator(_, ref substs, _)
| DefiningTy::InlineConst(_, ref substs) => {
// In the case of closures, we rely on the fact that
// the first N elements in the ClosureSubsts are
// inherited from the `closure_base_def_id`.
Expand Down Expand Up @@ -648,6 671,12 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
let ty = indices.fold_to_region_vids(tcx, ty);
ty::Binder::dummy(tcx.intern_type_list(&[ty]))
}

DefiningTy::InlineConst(def_id, substs) => {
assert_eq!(self.mir_def.did.to_def_id(), def_id);
let ty = substs.as_inline_const().ty();
ty::Binder::dummy(tcx.intern_type_list(&[ty]))
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 797,7 @@ rustc_queries! {
/// additional requirements that the closure's creator must verify.
query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> {
desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) }
cache_on_disk_if(tcx) { tcx.is_closure(key.to_def_id()) }
cache_on_disk_if(tcx) { tcx.is_closure_or_inline_const(key.to_def_id()) }
}
query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> {
desc {
Expand Down
79 changes: 68 additions & 11 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
@@ -1,7 1,9 @@
use crate::mir::interpret::ConstValue;
use crate::mir::interpret::{LitToConstInput, Scalar};
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::{ParamEnv, ParamEnvAnd};
use crate::ty::{
self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty,
TyCtxt, TypeFoldable,
};
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
Expand Down Expand Up @@ -54,6 56,24 @@ impl<'tcx> Const<'tcx> {

let ty = tcx.type_of(def.def_id_for_type_of());

match Self::try_eval_body_expr(tcx, ty, expr) {
Some(v) => v,
None => tcx.mk_const(ty::Const {
val: ty::ConstKind::Unevaluated(ty::Unevaluated {
def: def.to_global(),
substs_: None,
promoted: None,
}),
ty,
}),
}
}

fn try_eval_body_expr(
nbdd0121 marked this conversation as resolved.
Show resolved Hide resolved
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
) -> Option<&'tcx Self> {
let lit_input = match expr.kind {
hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind {
Expand All @@ -69,7 89,7 @@ impl<'tcx> Const<'tcx> {
// If an error occurred, ignore that it's a literal and leave reporting the error up to
// mir.
if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) {
return c;
return Some(c);
} else {
tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const");
}
Expand All @@ -85,7 105,7 @@ impl<'tcx> Const<'tcx> {
};

use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
let val = match expr.kind {
match expr.kind {
ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
// Find the name and index of the const parameter by indexing the generics of
// the parent item and construct a `ParamConst`.
Expand All @@ -95,16 115,53 @@ impl<'tcx> Const<'tcx> {
let generics = tcx.generics_of(item_def_id.to_def_id());
let index = generics.param_def_id_to_index[&def_id];
let name = tcx.hir().name(hir_id);
ty::ConstKind::Param(ty::ParamConst::new(index, name))
Some(tcx.mk_const(ty::Const {
val: ty::ConstKind::Param(ty::ParamConst::new(index, name)),
ty,
}))
}
_ => ty::ConstKind::Unevaluated(ty::Unevaluated {
def: def.to_global(),
substs_: None,
promoted: None,
}),
_ => None,
}
}

pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self {
debug!("Const::from_inline_const(def_id={:?})", def_id);

let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);

let body_id = match tcx.hir().get(hir_id) {
hir::Node::AnonConst(ac) => ac.body,
_ => span_bug!(
tcx.def_span(def_id.to_def_id()),
"from_inline_const can only process anonymous constants"
),
};

tcx.mk_const(ty::Const { val, ty })
let expr = &tcx.hir().body(body_id).value;

let ty = tcx.typeck(def_id).node_type(hir_id);

let ret = match Self::try_eval_body_expr(tcx, ty, expr) {
Some(v) => v,
None => {
let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id());
nbdd0121 marked this conversation as resolved.
Show resolved Hide resolved
let parent_substs =
tcx.erase_regions(InternalSubsts::identity_for_item(tcx, outer_def_id));
let substs =
InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems weird to erase regions above, given that ty is not erased here -- it'd be better to erase everything or nothing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty is from typeck().node_type() and is already erased.

.substs;
tcx.mk_const(ty::Const {
val: ty::ConstKind::Unevaluated(ty::Unevaluated {
def: ty::WithOptConstParam::unknown(def_id).to_global(),
substs_: Some(substs),
promoted: None,
}),
ty,
})
}
};
debug_assert!(!ret.has_free_regions(tcx));
ret
}

/// Interns the given value as a constant.
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 74,10 @@ pub use self::sty::{
Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, BoundVariableKind,
CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid, EarlyBoundRegion,
ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig,
GeneratorSubsts, GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection,
PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind,
RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind,
GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts, InlineConstSubstsParts, ParamConst,
ParamTy, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, PolyGenSig,
PolyTraitRef, ProjectionTy, Region, RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut,
UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind,
};
pub use self::trait_def::TraitDef;

Expand Down
60 changes: 60 additions & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 704,66 @@ impl<'tcx> UpvarSubsts<'tcx> {
}
}

/// An inline const is modeled like
///
/// const InlineConst<'l0...'li, T0...Tj, R>: R;
///
/// where:
///
/// - 'l0...'li and T0...Tj are the generic parameters
/// inherited from the item that defined the inline const,
/// - R represents the type of the constant.
///
/// When the inline const is instantiated, `R` is substituted as the actual inferred
/// type of the constant. The reason that `R` is represented as an extra type parameter
/// is the same reason that [`ClosureSubsts`] have `CS` and `U` as type parameters:
/// inline const can reference lifetimes that are internal to the creating function.
#[derive(Copy, Clone, Debug, TypeFoldable)]
pub struct InlineConstSubsts<'tcx> {
/// Generic parameters from the enclosing item,
/// concatenated with the inferred type of the constant.
pub substs: SubstsRef<'tcx>,
}

/// Struct returned by `split()`.
pub struct InlineConstSubstsParts<'tcx, T> {
pub parent_substs: &'tcx [GenericArg<'tcx>],
pub ty: T,
}

impl<'tcx> InlineConstSubsts<'tcx> {
/// Construct `InlineConstSubsts` from `InlineConstSubstsParts`.
pub fn new(
tcx: TyCtxt<'tcx>,
parts: InlineConstSubstsParts<'tcx, Ty<'tcx>>,
) -> InlineConstSubsts<'tcx> {
InlineConstSubsts {
substs: tcx.mk_substs(
parts.parent_substs.iter().copied().chain(std::iter::once(parts.ty.into())),
),
}
}

/// Divides the inline const substs into their respective components.
/// The ordering assumed here must match that used by `InlineConstSubsts::new` above.
fn split(self) -> InlineConstSubstsParts<'tcx, GenericArg<'tcx>> {
match self.substs[..] {
[ref parent_substs @ .., ty] => InlineConstSubstsParts { parent_substs, ty },
_ => bug!("inline const substs missing synthetics"),
}
}

/// Returns the substitutions of the inline const's parent.
pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] {
self.split().parent_substs
}

/// Returns the type of this inline const.
pub fn ty(self) -> Ty<'tcx> {
self.split().ty.expect_ty()
}
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable)]
pub enum ExistentialPredicate<'tcx> {
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_middle/src/ty/subst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 3,7 @@
use crate::mir;
use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::sty::{ClosureSubsts, GeneratorSubsts};
use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts};
use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};

use rustc_hir::def_id::DefId;
Expand Down Expand Up @@ -204,6 204,14 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
GeneratorSubsts { substs: self }
}

/// Interpret these substitutions as the substitutions of an inline const.
/// Inline const substitutions have a particular structure controlled by the
/// compiler that encodes information like the inferred type;
/// see `ty::InlineConstSubsts` struct for more comments.
pub fn as_inline_const(&'tcx self) -> InlineConstSubsts<'tcx> {
InlineConstSubsts { substs: self }
}

/// Creates an `InternalSubsts` that maps each generic parameter to itself.
pub fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> {
Self::for_item(tcx, def_id, |param, _| tcx.mk_param_from_def(param))
Expand Down
Loading