Skip to content

Change Thir to lazily create constants #94876

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

Merged
merged 7 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,7 @@ rustc_queries! {
desc { "get a &core::panic::Location referring to a span" }
}

// FIXME get rid of this with valtrees
query lit_to_const(
key: LitToConstInput<'tcx>
) -> Result<ty::Const<'tcx>, LitToConstError> {
Expand Down
31 changes: 25 additions & 6 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ pub enum ExprKind<'tcx> {
},
/// An inline `const` block, e.g. `const {}`.
ConstBlock {
value: Const<'tcx>,
did: DefId,
substs: SubstsRef<'tcx>,
},
/// An array literal constructed from one repeated element, e.g. `[1; 5]`.
Repeat {
Expand Down Expand Up @@ -408,13 +409,25 @@ pub enum ExprKind<'tcx> {
},
/// A literal.
Literal {
literal: Const<'tcx>,
lit: &'tcx hir::Lit,
neg: bool,
},
/// For literals that don't correspond to anything in the HIR
NonHirLiteral {
lit: ty::ScalarInt,
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
},
/// Associated constants and named constants
NamedConst {
def_id: DefId,
substs: SubstsRef<'tcx>,
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
/// The `DefId` of the `const` item this literal
/// was produced from, if this is not a user-written
/// literal value.
const_id: Option<DefId>,
},
ConstParam {
param: ty::ParamConst,
def_id: DefId,
},
// FIXME improve docs for `StaticRef` by distinguishing it from `NamedConst`
/// A literal containing the address of a `static`.
///
/// This is only distinguished from `Literal` so that we can register some
Expand All @@ -439,6 +452,12 @@ pub enum ExprKind<'tcx> {
},
}

impl<'tcx> ExprKind<'tcx> {
pub fn zero_sized_literal(user_ty: Option<Canonical<'tcx, UserType<'tcx>>>) -> Self {
ExprKind::NonHirLiteral { lit: ty::ScalarInt::ZST, user_ty }
}
}

/// Represents the association of a field identifier and an expression.
///
/// This is used in struct constructors.
Expand Down
20 changes: 8 additions & 12 deletions compiler/rustc_middle/src/thir/visit.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::{
Arm, Block, Expr, ExprKind, Guard, InlineAsmOperand, Pat, PatKind, Stmt, StmtKind, Thir,
};
use rustc_middle::ty::Const;

pub trait Visitor<'a, 'tcx: 'a>: Sized {
fn thir(&self) -> &'a Thir<'tcx>;
Expand All @@ -25,8 +24,6 @@ pub trait Visitor<'a, 'tcx: 'a>: Sized {
fn visit_pat(&mut self, pat: &Pat<'tcx>) {
walk_pat(self, pat);
}

fn visit_const(&mut self, _cnst: Const<'tcx>) {}
}

pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Expr<'tcx>) {
Expand Down Expand Up @@ -93,10 +90,9 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
visitor.visit_expr(&visitor.thir()[value])
}
}
ConstBlock { value } => visitor.visit_const(value),
Repeat { value, count } => {
ConstBlock { did: _, substs: _ } => {}
Repeat { value, count: _ } => {
visitor.visit_expr(&visitor.thir()[value]);
visitor.visit_const(count);
}
Array { ref fields } | Tuple { ref fields } => {
for &field in &**fields {
Expand All @@ -122,7 +118,10 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
visitor.visit_expr(&visitor.thir()[source])
}
Closure { closure_id: _, substs: _, upvars: _, movability: _, fake_reads: _ } => {}
Literal { literal, user_ty: _, const_id: _ } => visitor.visit_const(literal),
Literal { lit: _, neg: _ } => {}
NonHirLiteral { lit: _, user_ty: _ } => {}
NamedConst { def_id: _, substs: _, user_ty: _ } => {}
ConstParam { param: _, def_id: _ } => {}
StaticRef { alloc_id: _, ty: _, def_id: _ } => {}
InlineAsm { ref operands, template: _, options: _, line_spans: _ } => {
for op in &**operands {
Expand Down Expand Up @@ -209,11 +208,8 @@ pub fn walk_pat<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, pat: &Pat<'
visitor.visit_pat(&subpattern.pattern);
}
}
Constant { value } => visitor.visit_const(*value),
Range(range) => {
visitor.visit_const(range.lo);
visitor.visit_const(range.hi);
}
Constant { value: _ } => {}
Range(_) => {}
Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => {
for subpattern in prefix {
visitor.visit_pat(&subpattern);
Expand Down
118 changes: 108 additions & 10 deletions compiler/rustc_mir_build/src/build/expr/as_constant.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,141 @@
//! See docs in build/expr/mod.rs

use crate::build::Builder;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use crate::thir::constant::parse_float;
use rustc_ast::ast;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::interpret::{
Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar,
};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::CanonicalUserTypeAnnotation;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt};
use rustc_target::abi::Size;

impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, yielding a compile-time constant. Assumes that
/// `expr` is a valid compile-time constant!
crate fn as_constant(&mut self, expr: &Expr<'tcx>) -> Constant<'tcx> {
let create_uneval_from_def_id =
|tcx: TyCtxt<'tcx>, def_id: DefId, ty: Ty<'tcx>, substs: SubstsRef<'tcx>| {
let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
tcx.mk_const(ty::ConstS { val: ty::ConstKind::Unevaluated(uneval), ty })
};

let this = self;
let tcx = this.tcx;
let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
match *kind {
ExprKind::Scope { region_scope: _, lint_level: _, value } => {
this.as_constant(&this.thir[value])
}
ExprKind::Literal { literal, user_ty, const_id: _ } => {
ExprKind::Literal { lit, neg } => {
let literal =
match lit_to_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) {
Ok(c) => c,
Err(LitToConstError::Reported) => ConstantKind::Ty(tcx.const_error(ty)),
Err(LitToConstError::TypeError) => {
bug!("encountered type error in `lit_to_constant")
}
};

Constant { span, user_ty: None, literal: literal.into() }
}
ExprKind::NonHirLiteral { lit, user_ty } => {
let user_ty = user_ty.map(|user_ty| {
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
span,
user_ty,
inferred_ty: ty,
})
});
assert_eq!(literal.ty(), ty);
Constant { span, user_ty, literal: literal.into() }

let literal = ConstantKind::Val(ConstValue::Scalar(Scalar::Int(lit)), ty);

Constant { span, user_ty: user_ty, literal }
}
ExprKind::NamedConst { def_id, substs, user_ty } => {
let user_ty = user_ty.map(|user_ty| {
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
span,
user_ty,
inferred_ty: ty,
})
});
let literal = ConstantKind::Ty(create_uneval_from_def_id(tcx, def_id, ty, substs));

Constant { user_ty, span, literal }
}
ExprKind::ConstParam { param, def_id: _ } => {
let const_param =
tcx.mk_const(ty::ConstS { val: ty::ConstKind::Param(param), ty: expr.ty });
let literal = ConstantKind::Ty(const_param);

Constant { user_ty: None, span, literal }
}
ExprKind::ConstBlock { did: def_id, substs } => {
let literal = ConstantKind::Ty(create_uneval_from_def_id(tcx, def_id, ty, substs));

Constant { user_ty: None, span, literal }
}
ExprKind::StaticRef { alloc_id, ty, .. } => {
let const_val =
ConstValue::Scalar(Scalar::from_pointer(alloc_id.into(), &this.tcx));
let const_val = ConstValue::Scalar(Scalar::from_pointer(alloc_id.into(), &tcx));
let literal = ConstantKind::Val(const_val, ty);

Constant { span, user_ty: None, literal }
}
ExprKind::ConstBlock { value } => {
Constant { span: span, user_ty: None, literal: value.into() }
}
_ => span_bug!(span, "expression is not a valid constant {:?}", kind),
}
}
}

crate fn lit_to_constant<'tcx>(
tcx: TyCtxt<'tcx>,
lit_input: LitToConstInput<'tcx>,
) -> Result<ConstantKind<'tcx>, LitToConstError> {
let LitToConstInput { lit, ty, neg } = lit_input;
let trunc = |n| {
let param_ty = ty::ParamEnv::reveal_all().and(ty);
let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
let result = width.truncate(n);
trace!("trunc result: {}", result);
Ok(ConstValue::Scalar(Scalar::from_uint(result, width)))
};

let value = match (lit, &ty.kind()) {
(ast::LitKind::Str(s, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_str() => {
let s = s.as_str();
let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());
let allocation = tcx.intern_const_alloc(allocation);
ConstValue::Slice { data: allocation, start: 0, end: s.len() }
}
(ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _))
if matches!(inner_ty.kind(), ty::Slice(_)) =>
{
let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
let allocation = tcx.intern_const_alloc(allocation);
ConstValue::Slice { data: allocation, start: 0, end: data.len() }
}
(ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
let id = tcx.allocate_bytes(data);
ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
}
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
}
(ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?
}
(ast::LitKind::Float(n, _), ty::Float(fty)) => {
parse_float(*n, *fty, neg).ok_or(LitToConstError::Reported)?
}
(ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)),
(ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)),
(ast::LitKind::Err(_), _) => return Err(LitToConstError::Reported),
_ => return Err(LitToConstError::TypeError),
};

Ok(ConstantKind::Val(value, ty))
}
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/build/expr/as_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Continue { .. }
| ExprKind::Return { .. }
| ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ConstParam { .. }
| ExprKind::ConstBlock { .. }
| ExprKind::StaticRef { .. }
| ExprKind::InlineAsm { .. }
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
ExprKind::Yield { .. }
| ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ConstParam { .. }
| ExprKind::ConstBlock { .. }
| ExprKind::StaticRef { .. }
| ExprKind::Block { .. }
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/build/expr/as_temp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
local_decl.local_info =
Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: true }));
}
ExprKind::Literal { const_id: Some(def_id), .. } => {
ExprKind::NamedConst { def_id, .. } | ExprKind::ConstParam { def_id, .. } => {
local_decl.local_info = Some(Box::new(LocalInfo::ConstRef { def_id }));
}
_ => {}
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_mir_build/src/build/expr/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,12 @@ impl Category {
| ExprKind::AssignOp { .. }
| ExprKind::ThreadLocalRef(_) => Some(Category::Rvalue(RvalueFunc::AsRvalue)),

ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } | ExprKind::StaticRef { .. } => {
Some(Category::Constant)
}
ExprKind::ConstBlock { .. }
| ExprKind::Literal { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ConstParam { .. }
| ExprKind::StaticRef { .. }
| ExprKind::NamedConst { .. } => Some(Category::Constant),

ExprKind::Loop { .. }
| ExprKind::Block { .. }
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/build/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Closure { .. }
| ExprKind::ConstBlock { .. }
| ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ConstParam { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::StaticRef { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/build/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
//! basically the point where the "by value" operations are bridged
//! over to the "by reference" mode (`as_place`).

mod as_constant;
crate mod as_constant;
mod as_operand;
pub mod as_place;
mod as_rvalue;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::Block { .. }
| ExprKind::Borrow { .. }
| ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ConstParam { .. }
| ExprKind::ConstBlock { .. }
| ExprKind::Deref { .. }
| ExprKind::Index { .. }
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_mir_build/src/thir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustc_middle::ty::{self, ParamEnv, TyCtxt};
use rustc_span::symbol::Symbol;
use rustc_target::abi::Size;

// FIXME Once valtrees are available, get rid of this function and the query
crate fn lit_to_const<'tcx>(
tcx: TyCtxt<'tcx>,
lit_input: LitToConstInput<'tcx>,
Expand Down Expand Up @@ -57,7 +58,12 @@ crate fn lit_to_const<'tcx>(
Ok(ty::Const::from_value(tcx, lit, ty))
}

fn parse_float<'tcx>(num: Symbol, fty: ty::FloatTy, neg: bool) -> Option<ConstValue<'tcx>> {
// FIXME move this to rustc_mir_build::build
pub(crate) fn parse_float<'tcx>(
num: Symbol,
fty: ty::FloatTy,
neg: bool,
) -> Option<ConstValue<'tcx>> {
let num = num.as_str();
use rustc_apfloat::ieee::{Double, Single};
let scalar = match fty {
Expand Down
Loading