Auto merge of #29907 - nagisa:mir-moar-constants, r=nikomatsakis

Still will not translate references to items like `X` or `Y::V` where

```
struct X;
enum Y { V }
```

but I must go work on university things so I’m PRing what I have.

r? @nikomatsakis
This commit is contained in:
bors 2015-12-18 00:24:05 +00:00
commit 4eadabd9f8
14 changed files with 490 additions and 28 deletions

View file

@ -698,10 +698,20 @@ pub struct Constant<'tcx> {
pub literal: Literal<'tcx>,
}
#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, RustcDecodable)]
pub enum ItemKind {
Constant,
Function,
Struct,
Variant,
Method,
}
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
pub enum Literal<'tcx> {
Item {
def_id: DefId,
kind: ItemKind,
substs: &'tcx Substs<'tcx>,
},
Value {

View file

@ -66,6 +66,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
-> Operand<'tcx> {
let literal = Literal::Item {
def_id: item_ref.def_id,
kind: item_ref.kind,
substs: item_ref.substs,
};
self.literal_operand(span, item_ref.ty, literal)

View file

@ -480,6 +480,7 @@ fn method_callee<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>,
kind: ExprKind::Literal {
literal: Literal::Item {
def_id: callee.def_id,
kind: ItemKind::Method,
substs: callee.substs,
},
},
@ -514,16 +515,39 @@ fn convert_arm<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, arm: &'tcx hir::Arm) -> Arm<
fn convert_path_expr<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) -> ExprKind<'tcx> {
let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(expr.id).substs);
match cx.tcx.def_map.borrow()[&expr.id].full_def() {
// Otherwise there may be def_map borrow conflicts
let def = cx.tcx.def_map.borrow()[&expr.id].full_def();
match def {
def::DefVariant(_, def_id, false) |
def::DefStruct(def_id) |
def::DefFn(def_id, _) |
def::DefConst(def_id) |
def::DefMethod(def_id) |
def::DefAssociatedConst(def_id) =>
def::DefMethod(def_id) => {
let kind = match def {
def::DefVariant(..) => ItemKind::Variant,
def::DefStruct(..) => ItemKind::Struct,
def::DefFn(..) => ItemKind::Function,
def::DefMethod(..) => ItemKind::Method,
_ => panic!()
};
ExprKind::Literal {
literal: Literal::Item { def_id: def_id, substs: substs }
},
literal: Literal::Item { def_id: def_id, kind: kind, substs: substs }
}
},
def::DefConst(def_id) |
def::DefAssociatedConst(def_id) => {
if let Some(v) = cx.try_const_eval_literal(expr) {
ExprKind::Literal { literal: v }
} else {
ExprKind::Literal {
literal: Literal::Item {
def_id: def_id,
kind: ItemKind::Constant,
substs: substs
}
}
}
}
def::DefStatic(node_id, _) =>
ExprKind::StaticRef {

View file

@ -76,6 +76,13 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
Literal::Value { value: const_eval::eval_const_expr(self.tcx, e) }
}
pub fn try_const_eval_literal(&mut self, e: &hir::Expr) -> Option<Literal<'tcx>> {
let hint = const_eval::EvalHint::ExprTypeChecked;
const_eval::eval_const_expr_partial(self.tcx, e, hint, None)
.ok()
.map(|v| Literal::Value { value: v })
}
pub fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> {
let eq_def_id = self.tcx.lang_items.eq_trait().unwrap();
self.cmp_method_ref(eq_def_id, "eq", ty)
@ -132,6 +139,7 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> {
let method_ty = method_ty.ty.subst(self.tcx, &substs);
return ItemRef {
ty: method_ty,
kind: ItemKind::Method,
def_id: method.def_id,
substs: self.tcx.mk_substs(substs),
};

View file

@ -97,7 +97,11 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
Literal::Value { value: value }
} else {
let substs = self.cx.tcx.mk_substs(Substs::empty());
Literal::Item { def_id: def_id, substs: substs }
Literal::Item {
def_id: def_id,
kind: ItemKind::Constant,
substs: substs
}
};
PatternKind::Constant { value: literal }
}

View file

@ -14,7 +14,7 @@
//! unit-tested and separated from the Rust source and compiler data
//! structures.
use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp};
use rustc::mir::repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp, ItemKind};
use rustc::middle::def_id::DefId;
use rustc::middle::region::CodeExtent;
use rustc::middle::subst::Substs;
@ -29,6 +29,7 @@ pub mod cx;
#[derive(Clone, Debug)]
pub struct ItemRef<'tcx> {
pub ty: Ty<'tcx>,
pub kind: ItemKind,
pub def_id: DefId,
pub substs: &'tcx Substs<'tcx>,
}

View file

@ -108,12 +108,13 @@ pub fn const_lit(cx: &CrateContext, e: &hir::Expr, lit: &ast::Lit)
}
}
pub fn trans_constval<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
pub fn trans_constval<'blk, 'tcx>(bcx: common::Block<'blk, 'tcx>,
cv: &ConstVal,
ty: Ty<'tcx>,
param_substs: &'tcx Substs<'tcx>)
-> ValueRef
{
let ccx = bcx.ccx();
let llty = type_of::type_of(ccx, ty);
match *cv {
ConstVal::Float(v) => C_floating_f64(v, llty),
@ -123,21 +124,19 @@ pub fn trans_constval<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
ConstVal::ByteStr(ref v) => addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
ConstVal::Struct(id) | ConstVal::Tuple(id) => {
let expr = ccx.tcx().map.expect_expr(id);
let expr = bcx.tcx().map.expect_expr(id);
match const_expr(ccx, expr, param_substs, None, TrueConst::Yes) {
Ok((val, _)) => val,
Err(e) => panic!("const eval failure: {}", e.description()),
}
},
ConstVal::Array(id, _) | ConstVal::Repeat(id, _) => {
let expr = bcx.tcx().map.expect_expr(id);
expr::trans(bcx, expr).datum.val
},
ConstVal::Function(_) => {
unimplemented!()
},
ConstVal::Array(..) => {
unimplemented!()
},
ConstVal::Repeat(..) => {
unimplemented!()
},
}
}

View file

@ -472,7 +472,7 @@ fn trans_trait_callee_from_llval<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
///
/// In fact, all virtual calls can be thought of as normal trait calls
/// that go through this shim function.
fn trans_object_shim<'a, 'tcx>(
pub fn trans_object_shim<'a, 'tcx>(
ccx: &'a CrateContext<'a, 'tcx>,
upcast_trait_ref: ty::PolyTraitRef<'tcx>,
method_id: DefId,

View file

@ -14,7 +14,8 @@ use rustc::mir::repr as mir;
use trans::consts;
use trans::common::{self, Block};
use super::operand::OperandRef;
use super::operand::{OperandRef, OperandValue};
use super::MirContext;
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
@ -24,14 +25,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
ty: Ty<'tcx>)
-> OperandRef<'tcx>
{
use super::operand::OperandValue::{Ref, Immediate};
let ccx = bcx.ccx();
let val = consts::trans_constval(ccx, cv, ty, bcx.fcx.param_substs);
let val = consts::trans_constval(bcx, cv, ty, bcx.fcx.param_substs);
let val = if common::type_is_immediate(ccx, ty) {
Immediate(val)
OperandValue::Immediate(val)
} else {
Ref(val)
OperandValue::Ref(val)
};
assert!(!ty.has_erasable_regions());
@ -47,13 +46,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
constant: &mir::Constant<'tcx>)
-> OperandRef<'tcx>
{
let constant_ty = bcx.monomorphize(&constant.ty);
let ty = bcx.monomorphize(&constant.ty);
match constant.literal {
mir::Literal::Item { .. } => {
unimplemented!()
}
mir::Literal::Item { def_id, kind, substs } =>
self.trans_item_ref(bcx, ty, kind, substs, def_id),
mir::Literal::Value { ref value } => {
self.trans_constval(bcx, value, constant_ty)
self.trans_constval(bcx, value, ty)
}
}
}

View file

@ -0,0 +1,160 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Code for translating references to other items (DefIds).
use syntax::codemap::DUMMY_SP;
use rustc::front::map;
use rustc::middle::ty::{self, Ty, HasTypeFlags};
use rustc::middle::subst::Substs;
use rustc::middle::const_eval;
use rustc::middle::def_id::DefId;
use rustc::middle::subst;
use rustc::middle::traits;
use rustc::mir::repr::ItemKind;
use trans::common::{Block, fulfill_obligation};
use trans::base;
use trans::expr;
use trans::monomorphize;
use trans::meth;
use trans::inline;
use super::MirContext;
use super::operand::{OperandRef, OperandValue};
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
/// Translate reference to item.
pub fn trans_item_ref(&mut self,
bcx: Block<'bcx, 'tcx>,
ty: Ty<'tcx>,
kind: ItemKind,
substs: &'tcx Substs<'tcx>,
did: DefId)
-> OperandRef<'tcx> {
match kind {
ItemKind::Function |
ItemKind::Struct |
ItemKind::Variant => self.trans_fn_ref(bcx, ty, substs, did),
ItemKind::Method => match bcx.tcx().impl_or_trait_item(did).container() {
ty::ImplContainer(_) => self.trans_fn_ref(bcx, ty, substs, did),
ty::TraitContainer(tdid) => self.trans_static_method(bcx, ty, did, tdid, substs)
},
ItemKind::Constant => {
let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
let expr = const_eval::lookup_const_by_id(bcx.tcx(), did, None)
.expect("def was const, but lookup_const_by_id failed");
// FIXME: this is falling back to translating from HIR. This is not easy to fix,
// because we would have somehow adapt const_eval to work on MIR rather than HIR.
let d = expr::trans(bcx, expr);
OperandRef::from_rvalue_datum(d.datum.to_rvalue_datum(d.bcx, "").datum)
}
}
}
/// Translates references to a function-like items.
///
/// That includes regular functions, non-static methods, struct and enum variant constructors,
/// closures and possibly more.
///
/// This is an adaptation of callee::trans_fn_ref_with_substs.
pub fn trans_fn_ref(&mut self,
bcx: Block<'bcx, 'tcx>,
ty: Ty<'tcx>,
substs: &'tcx Substs<'tcx>,
did: DefId)
-> OperandRef<'tcx> {
let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
if !substs.types.is_empty() || is_named_tuple_constructor(bcx.tcx(), did) {
let (val, fn_ty, _) = monomorphize::monomorphic_fn(bcx.ccx(), did, substs, None);
// FIXME: cast fnptr to proper type if necessary
OperandRef {
ty: fn_ty,
val: OperandValue::Immediate(val)
}
} else {
let val = if let Some(node_id) = bcx.tcx().map.as_local_node_id(did) {
base::get_item_val(bcx.ccx(), node_id)
} else {
base::trans_external_path(bcx.ccx(), did, ty)
};
// FIXME: cast fnptr to proper type if necessary
OperandRef {
ty: ty,
val: OperandValue::Immediate(val)
}
}
}
/// Translates references to static methods.
///
/// This is an adaptation of meth::trans_static_method_callee
pub fn trans_static_method(&mut self,
bcx: Block<'bcx, 'tcx>,
ty: Ty<'tcx>,
method_id: DefId,
trait_id: DefId,
substs: &'tcx Substs<'tcx>)
-> OperandRef<'tcx> {
let ccx = bcx.ccx();
let tcx = bcx.tcx();
let mname = tcx.item_name(method_id);
let subst::SeparateVecsPerParamSpace {
types: rcvr_type,
selfs: rcvr_self,
fns: rcvr_method
} = substs.clone().types.split();
let trait_substs = Substs::erased(
subst::VecPerParamSpace::new(rcvr_type, rcvr_self, Vec::new())
);
let trait_substs = tcx.mk_substs(trait_substs);
let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, trait_substs));
let vtbl = fulfill_obligation(ccx, DUMMY_SP, trait_ref);
match vtbl {
traits::VtableImpl(traits::VtableImplData { impl_def_id, substs: imp_substs, .. }) => {
assert!(!imp_substs.types.needs_infer());
let subst::SeparateVecsPerParamSpace {
types: impl_type,
selfs: impl_self,
fns: _
} = imp_substs.types.split();
let callee_substs = Substs::erased(
subst::VecPerParamSpace::new(impl_type, impl_self, rcvr_method)
);
let mth = tcx.get_impl_method(impl_def_id, callee_substs, mname);
let mthsubsts = tcx.mk_substs(mth.substs);
self.trans_fn_ref(bcx, ty, mthsubsts, mth.method.def_id)
},
traits::VtableObject(ref data) => {
let idx = traits::get_vtable_index_of_object_method(tcx, data, method_id);
OperandRef::from_rvalue_datum(
meth::trans_object_shim(ccx, data.upcast_trait_ref.clone(), method_id, idx)
)
}
_ => {
tcx.sess.bug(&format!("static call to invalid vtable: {:?}", vtbl));
}
}
}
}
fn is_named_tuple_constructor(tcx: &ty::ctxt, def_id: DefId) -> bool {
let node_id = match tcx.map.as_local_node_id(def_id) {
Some(n) => n,
None => { return false; }
};
match tcx.map.find(node_id).expect("local item should be in ast map") {
map::NodeVariant(v) => {
v.node.data.is_tuple()
}
map::NodeStructCtor(_) => true,
_ => false
}
}

View file

@ -192,3 +192,4 @@ mod lvalue;
mod rvalue;
mod operand;
mod statement;
mod did;

View file

@ -76,6 +76,16 @@ impl<'tcx> OperandRef<'tcx> {
}
}
}
pub fn from_rvalue_datum(datum: datum::Datum<'tcx, datum::Rvalue>) -> OperandRef {
OperandRef {
ty: datum.ty,
val: match datum.kind.mode {
datum::RvalueMode::ByRef => OperandValue::Ref(datum.val),
datum::RvalueMode::ByValue => OperandValue::Immediate(datum.val),
}
}
}
}
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {

View file

@ -0,0 +1,28 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub struct S(pub u8);
impl S {
pub fn hey() -> u8 { 24 }
}
pub trait X {
fn hoy(&self) -> u8 { 25 }
}
impl X for S {}
pub enum E {
U(u8)
}
pub fn regular_fn() -> u8 { 12 }

View file

@ -0,0 +1,218 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_attrs)]
// aux-build:mir_external_refs.rs
extern crate mir_external_refs as ext;
struct S(u8);
impl S {
fn hey() -> u8 { 42 }
fn hey2(&self) -> u8 { 44 }
}
trait X {
fn hoy(&self) -> u8 { 43 }
fn hoy2() -> u8 { 45 }
}
trait F<U> {
fn f(self, other: U) -> u64;
}
impl F<u32> for u32 {
fn f(self, other: u32) -> u64 { self as u64 + other as u64 }
}
impl F<u64> for u32 {
fn f(self, other: u64) -> u64 { self as u64 - other }
}
impl F<u64> for u64 {
fn f(self, other: u64) -> u64 { self * other }
}
impl F<u32> for u64 {
fn f(self, other: u32) -> u64 { self ^ other as u64 }
}
trait T<I, O> {
fn staticmeth(i: I, o: O) -> (I, O) { (i, o) }
}
impl<I, O> T<I, O> for O {}
impl X for S {}
enum E {
U(u8)
}
const C: u8 = 84;
const C2: [u8; 5] = [42; 5];
const C3: [u8; 3] = [42, 41, 40];
fn regular() -> u8 {
21
}
fn parametric<T>(u: T) -> T {
u
}
#[rustc_mir]
fn t1() -> fn()->u8 {
regular
}
#[rustc_mir]
fn t2() -> fn(u8)->E {
E::U
}
#[rustc_mir]
fn t3() -> fn(u8)->S {
S
}
#[rustc_mir]
fn t4() -> fn()->u8 {
S::hey
}
#[rustc_mir]
fn t5() -> fn(&S)-> u8 {
<S as X>::hoy
}
#[rustc_mir]
fn t6() -> fn()->u8{
ext::regular_fn
}
#[rustc_mir]
fn t7() -> fn(u8)->ext::E {
ext::E::U
}
#[rustc_mir]
fn t8() -> fn(u8)->ext::S {
ext::S
}
#[rustc_mir]
fn t9() -> fn()->u8 {
ext::S::hey
}
#[rustc_mir]
fn t10() -> fn(&ext::S)->u8 {
<ext::S as ext::X>::hoy
}
#[rustc_mir]
fn t11() -> fn(u8)->u8 {
parametric
}
#[rustc_mir]
fn t12() -> u8 {
C
}
#[rustc_mir]
fn t13() -> [u8; 5] {
C2
}
#[rustc_mir]
fn t13_2() -> [u8; 3] {
C3
}
#[rustc_mir]
fn t14() -> fn()-> u8 {
<S as X>::hoy2
}
#[rustc_mir]
fn t15() -> fn(&S)-> u8 {
S::hey2
}
#[rustc_mir]
fn t16() -> fn(u32, u32)->u64 {
F::f
}
#[rustc_mir]
fn t17() -> fn(u32, u64)->u64 {
F::f
}
#[rustc_mir]
fn t18() -> fn(u64, u64)->u64 {
F::f
}
#[rustc_mir]
fn t19() -> fn(u64, u32)->u64 {
F::f
}
#[rustc_mir]
fn t20() -> fn(u64, u32)->(u64, u32) {
<u32 as T<_, _>>::staticmeth
}
fn main(){
unsafe {
assert_eq!(t1()(), regular());
assert!(::std::mem::transmute::<_, *mut ()>(t2()) ==
::std::mem::transmute::<_, *mut ()>(E::U));
assert!(::std::mem::transmute::<_, *mut ()>(t3()) ==
::std::mem::transmute::<_, *mut ()>(S));
assert_eq!(t4()(), S::hey());
let s = S(42);
assert_eq!(t5()(&s), <S as X>::hoy(&s));
assert_eq!(t6()(), ext::regular_fn());
assert!(::std::mem::transmute::<_, *mut ()>(t7()) ==
::std::mem::transmute::<_, *mut ()>(ext::E::U));
assert!(::std::mem::transmute::<_, *mut ()>(t8()) ==
::std::mem::transmute::<_, *mut ()>(ext::S));
assert_eq!(t9()(), ext::S::hey());
let sext = ext::S(6);
assert_eq!(t10()(&sext), <ext::S as ext::X>::hoy(&sext));
let p = parametric::<u8>;
assert!(::std::mem::transmute::<_, *mut ()>(t11()) ==
::std::mem::transmute::<_, *mut ()>(p));
assert_eq!(t12(), C);
assert_eq!(t13(), C2);
assert_eq!(t13_2(), C3);
assert_eq!(t14()(), <S as X>::hoy2());
assert_eq!(t15()(&s), S::hey2(&s));
assert_eq!(t16()(10u32, 20u32), F::f(10u32, 20u32));
assert_eq!(t17()(30u32, 10u64), F::f(30u32, 10u64));
assert_eq!(t18()(50u64, 5u64), F::f(50u64, 5u64));
assert_eq!(t19()(322u64, 2u32), F::f(322u64, 2u32));
assert_eq!(t20()(123u64, 38u32), <u32 as T<_, _>>::staticmeth(123, 38));
}
}