rustc: Add `const` globals to the language

This change is an implementation of [RFC 69][rfc] which adds a third kind of
global to the language, `const`. This global is most similar to what the old
`static` was, and if you're unsure about what to use then you should use a
`const`.

The semantics of these three kinds of globals are:

* A `const` does not represent a memory location, but only a value. Constants
  are translated as rvalues, which means that their values are directly inlined
  at usage location (similar to a #define in C/C++). Constant values are, well,
  constant, and can not be modified. Any "modification" is actually a
  modification to a local value on the stack rather than the actual constant
  itself.

  Almost all values are allowed inside constants, whether they have interior
  mutability or not. There are a few minor restrictions listed in the RFC, but
  they should in general not come up too often.

* A `static` now always represents a memory location (unconditionally). Any
  references to the same `static` are actually a reference to the same memory
  location. Only values whose types ascribe to `Sync` are allowed in a `static`.
  This restriction is in place because many threads may access a `static`
  concurrently. Lifting this restriction (and allowing unsafe access) is a
  future extension not implemented at this time.

* A `static mut` continues to always represent a memory location. All references
  to a `static mut` continue to be `unsafe`.

This is a large breaking change, and many programs will need to be updated
accordingly. A summary of the breaking changes is:

* Statics may no longer be used in patterns. Statics now always represent a
  memory location, which can sometimes be modified. To fix code, repurpose the
  matched-on-`static` to a `const`.

      static FOO: uint = 4;
      match n {
          FOO => { /* ... */ }
          _ => { /* ... */ }
      }

  change this code to:

      const FOO: uint = 4;
      match n {
          FOO => { /* ... */ }
          _ => { /* ... */ }
      }

* Statics may no longer refer to other statics by value. Due to statics being
  able to change at runtime, allowing them to reference one another could
  possibly lead to confusing semantics. If you are in this situation, use a
  constant initializer instead. Note, however, that statics may reference other
  statics by address, however.

* Statics may no longer be used in constant expressions, such as array lengths.
  This is due to the same restrictions as listed above. Use a `const` instead.

[breaking-change]

[rfc]: https://github.com/rust-lang/rfcs/pull/246
This commit is contained in:
Alex Crichton 2014-10-06 08:17:01 -07:00
parent a89ad58710
commit 90d03d7926
46 changed files with 722 additions and 446 deletions

View File

@ -978,7 +978,8 @@ impl LintPass for NonUppercaseStatics {
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
match it.node {
// only check static constants
ast::ItemStatic(_, ast::MutImmutable, _) => {
ast::ItemStatic(_, ast::MutImmutable, _) |
ast::ItemConst(..) => {
let s = token::get_ident(it.ident);
// check for lowercase letters rather than non-uppercase
// ones (some scripts don't have a concept of
@ -998,7 +999,7 @@ impl LintPass for NonUppercaseStatics {
fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
// Lint for constants that look like binding identifiers (#7526)
match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) {
(&ast::PatIdent(_, ref path1, _), Some(&def::DefStatic(_, false))) => {
(&ast::PatIdent(_, ref path1, _), Some(&def::DefConst(..))) => {
let s = token::get_ident(path1.node);
if s.get().chars().any(|c| c.is_lowercase()) {
cx.span_lint(NON_UPPERCASE_STATICS, path1.span,

View File

@ -126,12 +126,14 @@ enum Family {
Trait, // I
Struct, // S
PublicField, // g
InheritedField // N
InheritedField, // N
Constant, // C
}
fn item_family(item: rbml::Doc) -> Family {
let fam = reader::get_doc(item, tag_items_data_item_family);
match reader::doc_as_u8(fam) as char {
'C' => Constant,
'c' => ImmStatic,
'b' => MutStatic,
'f' => Fn,
@ -303,6 +305,7 @@ fn item_to_def_like(item: rbml::Doc, did: ast::DefId, cnum: ast::CrateNum)
-> DefLike {
let fam = item_family(item);
match fam {
Constant => DlDef(def::DefConst(did)),
ImmStatic => DlDef(def::DefStatic(did, false)),
MutStatic => DlDef(def::DefStatic(did, true)),
Struct => DlDef(def::DefStruct(did)),

View File

@ -69,7 +69,6 @@ pub struct EncodeParams<'a, 'tcx: 'a> {
pub tcx: &'a ty::ctxt<'tcx>,
pub reexports2: &'a middle::resolve::ExportMap2,
pub item_symbols: &'a RefCell<NodeMap<String>>,
pub non_inlineable_statics: &'a RefCell<NodeSet>,
pub link_meta: &'a LinkMeta,
pub cstore: &'a cstore::CStore,
pub encode_inlined_item: EncodeInlinedItem<'a>,
@ -81,7 +80,6 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
pub tcx: &'a ty::ctxt<'tcx>,
pub reexports2: &'a middle::resolve::ExportMap2,
pub item_symbols: &'a RefCell<NodeMap<String>>,
pub non_inlineable_statics: &'a RefCell<NodeSet>,
pub link_meta: &'a LinkMeta,
pub cstore: &'a cstore::CStore,
pub encode_inlined_item: RefCell<EncodeInlinedItem<'a>>,
@ -1069,12 +1067,20 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_symbol(ecx, rbml_w, item.id);
encode_name(rbml_w, item.ident.name);
encode_path(rbml_w, path);
let inlineable = !ecx.non_inlineable_statics.borrow().contains(&item.id);
if inlineable {
encode_inlined_item(ecx, rbml_w, IIItemRef(item));
}
encode_visibility(rbml_w, vis);
encode_stability(rbml_w, stab);
encode_attributes(rbml_w, item.attrs.as_slice());
rbml_w.end_tag();
}
ItemConst(_, _) => {
add_to_index(item, rbml_w, index);
rbml_w.start_tag(tag_items_data_item);
encode_def_id(rbml_w, def_id);
encode_family(rbml_w, 'C');
encode_bounds_and_type(rbml_w, ecx, &lookup_item_type(tcx, def_id));
encode_name(rbml_w, item.ident.name);
encode_path(rbml_w, path);
encode_inlined_item(ecx, rbml_w, IIItemRef(item));
encode_visibility(rbml_w, vis);
encode_stability(rbml_w, stab);
rbml_w.end_tag();
@ -2076,7 +2082,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter, parms: EncodeParams, krate:
cstore,
encode_inlined_item,
link_meta,
non_inlineable_statics,
reachable,
..
} = parms;
@ -2085,7 +2090,6 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter, parms: EncodeParams, krate:
tcx: tcx,
reexports2: reexports2,
item_symbols: item_symbols,
non_inlineable_statics: non_inlineable_statics,
link_meta: link_meta,
cstore: cstore,
encode_inlined_item: RefCell::new(encode_inlined_item),

View File

@ -460,6 +460,7 @@ impl tr for def::Def {
def::DefMod(did) => { def::DefMod(did.tr(dcx)) }
def::DefForeignMod(did) => { def::DefForeignMod(did.tr(dcx)) }
def::DefStatic(did, m) => { def::DefStatic(did.tr(dcx), m) }
def::DefConst(did) => { def::DefConst(did.tr(dcx)) }
def::DefLocal(nid) => { def::DefLocal(dcx.tr_id(nid)) }
def::DefVariant(e_did, v_did, is_s) => {
def::DefVariant(e_did.tr(dcx), v_did.tr(dcx), is_s)

View File

@ -169,15 +169,11 @@ fn check_aliasability(bccx: &BorrowckCtxt,
// Borrow of an immutable static item:
match safety {
mc::InteriorUnsafe => {
// If the static item contains an Unsafe<T>, it has interior mutability.
// In such cases, we cannot permit it to be borrowed, because the
// static item resides in immutable memory and mutating it would
// cause segfaults.
bccx.tcx.sess.span_err(borrow_span,
"borrow of immutable static items \
with unsafe interior is not \
allowed");
Err(())
// If the static item contains an Unsafe<T>, it has interior
// mutability. In such cases, another phase of the compiler
// will ensure that the type is `Sync` and then trans will
// not put it in rodata, so this is ok to allow.
Ok(())
}
mc::InteriorSafe => {
// Immutable static can be borrowed, no problem.

View File

@ -106,7 +106,8 @@ fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
// loan step is intended for things that have a data
// flow dependent conditions.
match item.node {
ast::ItemStatic(_, _, ref ex) => {
ast::ItemStatic(_, _, ref ex) |
ast::ItemConst(_, ref ex) => {
gather_loans::gather_loans_in_static_initializer(this, &**ex);
}
_ => {

View File

@ -48,7 +48,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
}
fn visit_expr(&mut self, ex: &Expr) {
if check_expr(self, ex) {
visit::walk_expr(v, e);
visit::walk_expr(self, ex);
}
}
}
@ -61,7 +61,8 @@ pub fn check_crate(tcx: &ty::ctxt) {
fn check_item(v: &mut CheckCrateVisitor, it: &Item) {
match it.node {
ItemStatic(_, _, ref ex) => {
ItemStatic(_, _, ref ex) |
ItemConst(_, ref ex) => {
v.inside_const(|v| v.visit_expr(&**ex));
}
ItemEnum(ref enum_definition, _) => {
@ -138,6 +139,7 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr) -> bool {
}
match v.tcx.def_map.borrow().find(&e.id) {
Some(&DefStatic(..)) |
Some(&DefConst(..)) |
Some(&DefFn(..)) |
Some(&DefVariant(_, _, _)) |
Some(&DefStruct(_)) => { }
@ -190,7 +192,7 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr) -> bool {
}
}
match block.expr {
Some(ref expr) => check_expr(v, &**expr),
Some(ref expr) => { check_expr(v, &**expr); }
None => {}
}
}

View File

@ -32,7 +32,7 @@ use syntax::ptr::P;
use syntax::visit::{mod, Visitor, FnKind};
use util::ppaux::ty_to_string;
static DUMMY_WILD_PAT: Pat = Pat {
pub const DUMMY_WILD_PAT: Pat = Pat {
id: DUMMY_NODE_ID,
node: PatWild(PatWildSingle),
span: DUMMY_SP
@ -299,9 +299,10 @@ fn raw_pat<'a>(p: &'a Pat) -> &'a Pat {
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix) {
match is_useful(cx, matrix, &[&DUMMY_WILD_PAT], ConstructWitness) {
UsefulWithWitness(pats) => {
let dummy = DUMMY_WILD_PAT.clone();
let witness = match pats.as_slice() {
[ref witness] => &**witness,
[] => &DUMMY_WILD_PAT,
[] => &dummy,
_ => unreachable!()
};
span_err!(cx.tcx.sess, sp, E0004,
@ -349,7 +350,7 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
PatIdent(..) | PatEnum(..) => {
let def = self.tcx.def_map.borrow().find_copy(&pat.id);
match def {
Some(DefStatic(did, _)) => match lookup_const_by_id(self.tcx, did) {
Some(DefConst(did)) => match lookup_const_by_id(self.tcx, did) {
Some(const_expr) => {
const_expr_to_pat(self.tcx, const_expr).map(|mut new_pat| {
new_pat.span = pat.span;
@ -359,7 +360,7 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
None => {
self.failed = true;
span_err!(self.tcx.sess, pat.span, E0158,
"extern statics cannot be referenced in patterns");
"statics cannot be referenced in patterns");
pat
}
},
@ -555,8 +556,9 @@ fn is_useful(cx: &MatchCheckCtxt,
let arity = constructor_arity(cx, &c, left_ty);
let mut result = {
let pat_slice = pats.as_slice();
let dummy = DUMMY_WILD_PAT.clone();
let subpats = Vec::from_fn(arity, |i| {
pat_slice.get(i).map_or(&DUMMY_WILD_PAT, |p| &**p)
pat_slice.get(i).map_or(&dummy, |p| &**p)
});
vec![construct_witness(cx, &c, subpats, left_ty)]
};
@ -578,8 +580,9 @@ fn is_useful(cx: &MatchCheckCtxt,
}).collect();
match is_useful(cx, &matrix, v.tail(), witness) {
UsefulWithWitness(pats) => {
let dummy = DUMMY_WILD_PAT.clone();
let arity = constructor_arity(cx, &constructor, left_ty);
let wild_pats = Vec::from_elem(arity, &DUMMY_WILD_PAT);
let wild_pats = Vec::from_elem(arity, &dummy);
let enum_pat = construct_witness(cx, &constructor, wild_pats, left_ty);
let mut new_pats = vec![enum_pat];
new_pats.extend(pats.into_iter());
@ -600,10 +603,11 @@ fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix,
v: &[&Pat], ctor: Constructor, lty: ty::t,
witness: WitnessPreference) -> Usefulness {
let arity = constructor_arity(cx, &ctor, lty);
let dummy = DUMMY_WILD_PAT.clone();
let matrix = Matrix(m.iter().filter_map(|r| {
specialize(cx, r.as_slice(), &ctor, 0u, arity)
specialize(cx, r.as_slice(), &dummy, &ctor, 0u, arity)
}).collect());
match specialize(cx, v, &ctor, 0u, arity) {
match specialize(cx, v, &dummy, &ctor, 0u, arity) {
Some(v) => is_useful(cx, &matrix, v.as_slice(), witness),
None => NotUseful
}
@ -624,23 +628,26 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
match pat.node {
PatIdent(..) =>
match cx.tcx.def_map.borrow().find(&pat.id) {
Some(&DefStatic(..)) =>
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
Some(&DefConst(..)) =>
cx.tcx.sess.span_bug(pat.span, "const pattern should've \
been rewritten"),
Some(&DefStruct(_)) => vec!(Single),
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!()
},
PatEnum(..) =>
match cx.tcx.def_map.borrow().find(&pat.id) {
Some(&DefStatic(..)) =>
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
Some(&DefConst(..)) =>
cx.tcx.sess.span_bug(pat.span, "static pattern should've \
been rewritten"),
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!(Single)
},
PatStruct(..) =>
match cx.tcx.def_map.borrow().find(&pat.id) {
Some(&DefStatic(..)) =>
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
Some(&DefConst(..)) =>
cx.tcx.sess.span_bug(pat.span, "static pattern should've \
been rewritten"),
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!(Single)
},
@ -722,7 +729,7 @@ fn range_covered_by_constructor(ctor: &Constructor,
/// different patterns.
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
/// fields filled with wild patterns.
pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], dummy: &'a Pat,
constructor: &Constructor, col: uint, arity: uint) -> Option<Vec<&'a Pat>> {
let &Pat {
id: pat_id, node: ref node, span: pat_span
@ -730,32 +737,34 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
let head: Option<Vec<&Pat>> = match node {
&PatWild(_) =>
Some(Vec::from_elem(arity, &DUMMY_WILD_PAT)),
Some(Vec::from_elem(arity, dummy)),
&PatIdent(_, _, _) => {
let opt_def = cx.tcx.def_map.borrow().find_copy(&pat_id);
match opt_def {
Some(DefStatic(..)) =>
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
Some(DefConst(..)) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
Some(DefVariant(_, id, _)) => if *constructor == Variant(id) {
Some(vec!())
} else {
None
},
_ => Some(Vec::from_elem(arity, &DUMMY_WILD_PAT))
_ => Some(Vec::from_elem(arity, dummy))
}
}
&PatEnum(_, ref args) => {
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
match def {
DefStatic(..) =>
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
DefConst(..) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
DefVariant(_, id, _) if *constructor != Variant(id) => None,
DefVariant(..) | DefStruct(..) => {
Some(match args {
&Some(ref args) => args.iter().map(|p| &**p).collect(),
&None => Vec::from_elem(arity, &DUMMY_WILD_PAT)
&None => Vec::from_elem(arity, dummy)
})
}
_ => None
@ -766,8 +775,9 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
// Is this a struct or an enum variant?
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
let class_id = match def {
DefStatic(..) =>
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
DefConst(..) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) {
Some(variant_id)
} else {
@ -790,7 +800,7 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
let args = struct_fields.iter().map(|sf| {
match pattern_fields.iter().find(|f| f.ident.name == sf.name) {
Some(ref f) => &*f.pat,
_ => &DUMMY_WILD_PAT
_ => dummy
}
}).collect();
args
@ -833,13 +843,13 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
// Fixed-length vectors.
Single => {
let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
pats.grow_fn(arity - before.len() - after.len(), |_| &DUMMY_WILD_PAT);
pats.grow_fn(arity - before.len() - after.len(), |_| dummy);
pats.extend(after.iter().map(|p| &**p));
Some(pats)
},
Slice(length) if before.len() + after.len() <= length && slice.is_some() => {
let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
pats.grow_fn(arity - before.len() - after.len(), |_| &DUMMY_WILD_PAT);
pats.grow_fn(arity - before.len() - after.len(), |_| dummy);
pats.extend(after.iter().map(|p| &**p));
Some(pats)
},

View File

@ -25,48 +25,103 @@
// by borrowck::gather_loans
use middle::ty;
use middle::def;
use middle::typeck;
use middle::traits;
use middle::mem_categorization as mc;
use middle::expr_use_visitor as euv;
use util::nodemap::NodeSet;
use syntax::ast;
use syntax::visit::Visitor;
use syntax::visit;
use syntax::print::pprust;
use syntax::visit::Visitor;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::visit;
fn safe_type_for_static_mut(cx: &ty::ctxt, e: &ast::Expr) -> Option<String> {
let node_ty = ty::node_id_to_type(cx, e.id);
let tcontents = ty::type_contents(cx, node_ty);
debug!("safe_type_for_static_mut(dtor={}, managed={}, owned={})",
tcontents.has_dtor(), tcontents.owns_managed(), tcontents.owns_owned())
let suffix = if tcontents.has_dtor() {
"destructors"
} else if tcontents.owns_managed() {
"managed pointers"
} else if tcontents.owns_owned() {
"owned pointers"
} else {
return None;
};
Some(format!("mutable static items are not allowed to have {}", suffix))
#[deriving(Eq, PartialEq)]
enum Mode {
InConstant,
InStatic,
InStaticMut,
InNothing,
}
struct CheckStaticVisitor<'a, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>,
in_const: bool
mode: Mode,
checker: &'a mut GlobalChecker,
}
struct GlobalVisitor<'a, 'b, 't: 'b>(euv::ExprUseVisitor<'a, 'b, ty::ctxt<'t>>);
struct GlobalChecker {
static_consumptions: NodeSet,
const_borrows: NodeSet,
static_interior_borrows: NodeSet,
}
pub fn check_crate(tcx: &ty::ctxt) {
visit::walk_crate(&mut CheckStaticVisitor { tcx: tcx, in_const: false },
tcx.map.krate())
let mut checker = GlobalChecker {
static_consumptions: NodeSet::new(),
const_borrows: NodeSet::new(),
static_interior_borrows: NodeSet::new(),
};
{
let visitor = euv::ExprUseVisitor::new(&mut checker, tcx);
visit::walk_crate(&mut GlobalVisitor(visitor), tcx.map.krate());
}
visit::walk_crate(&mut CheckStaticVisitor {
tcx: tcx,
mode: InNothing,
checker: &mut checker,
}, tcx.map.krate());
}
impl<'a, 'tcx> CheckStaticVisitor<'a, 'tcx> {
fn with_const(&mut self, in_const: bool, f: |&mut CheckStaticVisitor<'a, 'tcx>|) {
let was_const = self.in_const;
self.in_const = in_const;
fn with_mode(&mut self, mode: Mode, f: |&mut CheckStaticVisitor<'a, 'tcx>|) {
let old = self.mode;
self.mode = mode;
f(self);
self.in_const = was_const;
self.mode = old;
}
fn msg(&self) -> &'static str {
match self.mode {
InConstant => "constants",
InStaticMut | InStatic => "statics",
InNothing => unreachable!(),
}
}
fn check_static_mut_type(&self, e: &ast::Expr) {
let node_ty = ty::node_id_to_type(self.tcx, e.id);
let tcontents = ty::type_contents(self.tcx, node_ty);
let suffix = if tcontents.has_dtor() {
"destructors"
} else if tcontents.owns_owned() {
"owned pointers"
} else {
return
};
self.tcx.sess.span_err(e.span, format!("mutable statics are not allowed \
to have {}", suffix).as_slice());
}
fn check_static_type(&self, e: &ast::Expr) {
let ty = ty::node_id_to_type(self.tcx, e.id);
let infcx = typeck::infer::new_infer_ctxt(self.tcx);
let mut fulfill_cx = traits::FulfillmentContext::new();
let cause = traits::ObligationCause::misc(DUMMY_SP);
let obligation = traits::obligation_for_builtin_bound(self.tcx, cause, ty,
ty::BoundSync);
fulfill_cx.register_obligation(self.tcx, obligation.unwrap());
let env = ty::empty_parameter_environment();
let result = fulfill_cx.select_all_or_error(&infcx, &env, self.tcx).is_ok();
if !result {
self.tcx.sess.span_err(e.span, "shared static items must have a \
type which implements Sync");
}
}
}
@ -74,22 +129,20 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckStaticVisitor<'a, 'tcx> {
fn visit_item(&mut self, i: &ast::Item) {
debug!("visit_item(item={})", pprust::item_to_string(i));
match i.node {
ast::ItemStatic(_, mutability, ref expr) => {
match mutability {
ast::MutImmutable => {
self.with_const(true, |v| v.visit_expr(&**expr));
}
ast::MutMutable => {
match safe_type_for_static_mut(self.tcx, &**expr) {
Some(msg) => {
self.tcx.sess.span_err(expr.span, msg.as_slice());
}
None => {}
}
}
}
ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
self.check_static_type(&**expr);
self.with_mode(InStatic, |v| v.visit_expr(&**expr));
}
ast::ItemStatic(_, ast::MutMutable, ref expr) => {
self.check_static_mut_type(&**expr);
self.with_mode(InStaticMut, |v| v.visit_expr(&**expr));
}
ast::ItemConst(_, ref expr) => {
self.with_mode(InConstant, |v| v.visit_expr(&**expr));
}
_ => {
self.with_mode(InNothing, |v| visit::walk_item(v, i));
}
_ => self.with_const(false, |v| visit::walk_item(v, i))
}
}
@ -100,42 +153,170 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckStaticVisitor<'a, 'tcx> {
/// of a static item, this method does nothing but walking
/// down through it.
fn visit_expr(&mut self, e: &ast::Expr) {
debug!("visit_expr(expr={})", pprust::expr_to_string(e));
if !self.in_const {
if self.mode == InNothing {
return visit::walk_expr(self, e);
}
match e.node {
ast::ExprField(..) | ast::ExprTupField(..) | ast::ExprVec(..) |
ast::ExprBlock(..) | ast::ExprTup(..) => {
visit::walk_expr(self, e);
let node_ty = ty::node_id_to_type(self.tcx, e.id);
match ty::get(node_ty).sty {
ty::ty_struct(did, _) |
ty::ty_enum(did, _) if ty::has_dtor(self.tcx, did) => {
self.tcx.sess.span_err(e.span,
format!("{} are not allowed to have \
destructors", self.msg()).as_slice())
}
_ => {}
}
// statics cannot be consumed by value at any time, that would imply
// that they're an initializer (what a const is for) or kept in sync
// over time (not feasible), so deny it outright.
if self.checker.static_consumptions.remove(&e.id) {
self.tcx.sess.span_err(e.span, "cannot refer to other statics by \
value, use the address-of operator \
or a constant instead");
}
// Borrowed statics can specifically *only* have their address taken,
// not any number of other borrows such as borrowing fields, reading
// elements of an array, etc.
if self.checker.static_interior_borrows.remove(&e.id) {
self.tcx.sess.span_err(e.span, "cannot refer to the interior of \
another static, use a constant \
instead");
}
// constants cannot be borrowed if they contain interior mutability as
// it means that our "silent insertion of statics" could change
// initializer values (very bad).
if self.checker.const_borrows.remove(&e.id) {
let node_ty = ty::node_id_to_type(self.tcx, e.id);
let tcontents = ty::type_contents(self.tcx, node_ty);
if tcontents.interior_unsafe() {
self.tcx.sess.span_err(e.span, "cannot borrow a constant which \
contains interior mutability, \
create a static instead");
}
}
match e.node {
ast::ExprAddrOf(ast::MutMutable, _) => {
span_err!(self.tcx.sess, e.span, E0020,
"static items are not allowed to have mutable slices");
if self.mode != InStaticMut {
span_err!(self.tcx.sess, e.span, E0020,
"{} are not allowed to have mutable references",
self.msg());
}
},
ast::ExprBox(..) |
ast::ExprUnary(ast::UnUniq, _) => {
span_err!(self.tcx.sess, e.span, E0022,
"static items are not allowed to have custom pointers");
"{} are not allowed to have custom pointers",
self.msg());
}
_ => {
let node_ty = ty::node_id_to_type(self.tcx, e.id);
match ty::get(node_ty).sty {
ty::ty_struct(did, _) |
ty::ty_enum(did, _) => {
if ty::has_dtor(self.tcx, did) {
self.tcx.sess.span_err(e.span,
"static items are not allowed to have destructors");
return;
}
ast::ExprPath(..) => {
match ty::resolve_expr(self.tcx, e) {
def::DefStatic(..) if self.mode == InConstant => {
let msg = "constants cannot refer to other statics, \
insert an intermediate constant \
instead";
self.tcx.sess.span_err(e.span, msg.as_slice());
}
_ => {}
}
visit::walk_expr(self, e);
}
_ => {}
}
visit::walk_expr(self, e);
}
}
impl<'a, 'b, 't, 'v> Visitor<'v> for GlobalVisitor<'a, 'b, 't> {
fn visit_item(&mut self, item: &ast::Item) {
match item.node {
ast::ItemConst(_, ref e) |
ast::ItemStatic(_, _, ref e) => {
let GlobalVisitor(ref mut v) = *self;
v.consume_expr(&**e);
}
_ => {}
}
visit::walk_item(self, item);
}
}
impl euv::Delegate for GlobalChecker {
fn consume(&mut self,
consume_id: ast::NodeId,
_consume_span: Span,
cmt: mc::cmt,
_mode: euv::ConsumeMode) {
let mut cur = &cmt;
loop {
match cur.cat {
mc::cat_static_item => {
self.static_consumptions.insert(consume_id);
break
}
mc::cat_deref(ref cmt, _, _) |
mc::cat_discr(ref cmt, _) |
mc::cat_downcast(ref cmt) |
mc::cat_interior(ref cmt, _) => cur = cmt,
mc::cat_rvalue(..) |
mc::cat_copied_upvar(..) |
mc::cat_upvar(..) |
mc::cat_local(..) => break,
}
}
}
fn borrow(&mut self,
borrow_id: ast::NodeId,
_borrow_span: Span,
cmt: mc::cmt,
_loan_region: ty::Region,
_bk: ty::BorrowKind,
_loan_cause: euv::LoanCause) {
let mut cur = &cmt;
let mut is_interior = false;
loop {
match cur.cat {
mc::cat_rvalue(..) => {
self.const_borrows.insert(borrow_id);
break
}
mc::cat_static_item => {
if is_interior {
self.static_interior_borrows.insert(borrow_id);
}
break
}
mc::cat_deref(ref cmt, _, _) |
mc::cat_interior(ref cmt, _) => {
is_interior = true;
cur = cmt;
}
mc::cat_downcast(..) |
mc::cat_discr(..) |
mc::cat_copied_upvar(..) |
mc::cat_upvar(..) |
mc::cat_local(..) => unreachable!(),
}
}
}
fn decl_without_init(&mut self,
_id: ast::NodeId,
_span: Span) {}
fn mutate(&mut self,
_assignment_id: ast::NodeId,
_assignment_span: Span,
_assignee_cmt: mc::cmt,
_mode: euv::MutateMode) {}
fn consume_pat(&mut self,
_consume_pat: &ast::Pat,
_cmt: mc::cmt,
_mode: euv::ConsumeMode) {}
}

View File

@ -13,9 +13,9 @@
use driver::session::Session;
use middle::resolve;
use middle::def::DefStatic;
use middle::def::{DefStatic, DefConst};
use syntax::ast::{Crate, Expr, ExprPath, Item, ItemStatic, NodeId};
use syntax::ast;
use syntax::{ast_util, ast_map};
use syntax::visit::Visitor;
use syntax::visit;
@ -27,13 +27,13 @@ struct CheckCrateVisitor<'a, 'ast: 'a> {
}
impl<'v, 'a, 'ast> Visitor<'v> for CheckCrateVisitor<'a, 'ast> {
fn visit_item(&mut self, i: &Item) {
fn visit_item(&mut self, i: &ast::Item) {
check_item(self, i);
}
}
pub fn check_crate<'ast>(sess: &Session,
krate: &Crate,
krate: &ast::Crate,
def_map: &resolve::DefMap,
ast_map: &ast_map::Map<'ast>) {
let mut visitor = CheckCrateVisitor {
@ -45,9 +45,10 @@ pub fn check_crate<'ast>(sess: &Session,
sess.abort_if_errors();
}
fn check_item(v: &mut CheckCrateVisitor, it: &Item) {
fn check_item(v: &mut CheckCrateVisitor, it: &ast::Item) {
match it.node {
ItemStatic(_, _, ref ex) => {
ast::ItemStatic(_, _, ref ex) |
ast::ItemConst(_, ref ex) => {
check_item_recursion(v.sess, v.ast_map, v.def_map, it);
visit::walk_expr(v, &**ex)
},
@ -56,11 +57,11 @@ fn check_item(v: &mut CheckCrateVisitor, it: &Item) {
}
struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
root_it: &'a Item,
root_it: &'a ast::Item,
sess: &'a Session,
ast_map: &'a ast_map::Map<'ast>,
def_map: &'a resolve::DefMap,
idstack: Vec<NodeId>
idstack: Vec<ast::NodeId>
}
// Make sure a const item doesn't recursively refer to itself
@ -68,7 +69,7 @@ struct CheckItemRecursionVisitor<'a, 'ast: 'a> {
pub fn check_item_recursion<'a>(sess: &'a Session,
ast_map: &'a ast_map::Map,
def_map: &'a resolve::DefMap,
it: &'a Item) {
it: &'a ast::Item) {
let mut visitor = CheckItemRecursionVisitor {
root_it: it,
@ -81,7 +82,7 @@ pub fn check_item_recursion<'a>(sess: &'a Session,
}
impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
fn visit_item(&mut self, it: &Item) {
fn visit_item(&mut self, it: &ast::Item) {
if self.idstack.iter().any(|x| x == &(it.id)) {
self.sess.span_err(self.root_it.span, "recursive constant");
return;
@ -91,11 +92,12 @@ impl<'a, 'ast, 'v> Visitor<'v> for CheckItemRecursionVisitor<'a, 'ast> {
self.idstack.pop();
}
fn visit_expr(&mut self, e: &Expr) {
fn visit_expr(&mut self, e: &ast::Expr) {
match e.node {
ExprPath(..) => {
ast::ExprPath(..) => {
match self.def_map.borrow().find(&e.id) {
Some(&DefStatic(def_id, _)) if
Some(&DefStatic(def_id, _)) |
Some(&DefConst(def_id)) if
ast_util::is_local(def_id) => {
self.visit_item(&*self.ast_map.expect_item(def_id.node));
}

View File

@ -87,7 +87,7 @@ pub fn join_all<It: Iterator<constness>>(mut cs: It) -> constness {
fn lookup_const<'a>(tcx: &'a ty::ctxt, e: &Expr) -> Option<&'a Expr> {
let opt_def = tcx.def_map.borrow().find_copy(&e.id);
match opt_def {
Some(def::DefStatic(def_id, false)) => {
Some(def::DefConst(def_id)) => {
lookup_const_by_id(tcx, def_id)
}
Some(def::DefVariant(enum_def, variant_def, _)) => {
@ -155,7 +155,7 @@ pub fn lookup_const_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId)
match tcx.map.find(def_id.node) {
None => None,
Some(ast_map::NodeItem(it)) => match it.node {
ItemStatic(_, ast::MutImmutable, ref const_expr) => {
ItemConst(_, ref const_expr) => {
Some(&**const_expr)
}
_ => None
@ -173,7 +173,7 @@ pub fn lookup_const_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId)
let expr_id = match csearch::maybe_get_item_ast(tcx, def_id,
|a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
csearch::found(&ast::IIItem(ref item)) => match item.node {
ItemStatic(_, ast::MutImmutable, ref const_expr) => Some(const_expr.id),
ItemConst(_, ref const_expr) => Some(const_expr.id),
_ => None
},
_ => None

View File

@ -215,7 +215,8 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
ast::ItemFn(..)
| ast::ItemEnum(..)
| ast::ItemTy(..)
| ast::ItemStatic(..) => {
| ast::ItemStatic(..)
| ast::ItemConst(..) => {
visit::walk_item(self, &*item);
}
_ => ()

View File

@ -20,6 +20,7 @@ pub enum Def {
DefMod(ast::DefId),
DefForeignMod(ast::DefId),
DefStatic(ast::DefId, bool /* is_mutbl */),
DefConst(ast::DefId),
DefLocal(ast::NodeId),
DefVariant(ast::DefId /* enum */, ast::DefId /* variant */, bool /* is_structure */),
DefTy(ast::DefId, bool /* is_enum */),
@ -61,7 +62,7 @@ impl Def {
DefForeignMod(id) | DefStatic(id, _) |
DefVariant(_, id, _) | DefTy(id, _) | DefAssociatedTy(id) |
DefTyParam(_, id, _) | DefUse(id) | DefStruct(id) | DefTrait(id) |
DefMethod(id, _) => {
DefMethod(id, _) | DefConst(id) => {
id
}
DefLocal(id) |

View File

@ -267,7 +267,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
}
}
fn consume_expr(&mut self, expr: &ast::Expr) {
pub fn consume_expr(&mut self, expr: &ast::Expr) {
debug!("consume_expr(expr={})", expr.repr(self.tcx()));
let cmt = return_if_err!(self.mc.cat_expr(expr));

View File

@ -544,7 +544,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
match def {
def::DefStruct(..) | def::DefVariant(..) | def::DefFn(..) |
def::DefStaticMethod(..) => {
def::DefStaticMethod(..) | def::DefConst(..) => {
Ok(self.cat_rvalue_node(id, span, expr_ty))
}
def::DefMod(_) | def::DefForeignMod(_) | def::DefUse(_) |
@ -1104,7 +1104,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|x,y,z| op(x,y,z)));
}
}
Some(&def::DefStatic(..)) => {
Some(&def::DefConst(..)) => {
for subpat in subpats.iter() {
if_ok!(self.cat_pattern(cmt.clone(), &**subpat, |x,y,z| op(x,y,z)));
}

View File

@ -46,7 +46,7 @@ pub fn pat_is_const(dm: &resolve::DefMap, pat: &Pat) -> bool {
match pat.node {
PatIdent(_, _, None) | PatEnum(..) => {
match dm.borrow().find(&pat.id) {
Some(&DefStatic(_, false)) => true,
Some(&DefConst(..)) => true,
_ => false
}
}

View File

@ -805,6 +805,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
def::DefStaticMethod(..) => ck("static method"),
def::DefFn(..) => ck("function"),
def::DefStatic(..) => ck("static"),
def::DefConst(..) => ck("const"),
def::DefVariant(..) => ck("variant"),
def::DefTy(_, false) => ck("type"),
def::DefTy(_, true) => ck("enum"),
@ -1181,7 +1182,7 @@ impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> {
}
}
ast::ItemStatic(..) | ast::ItemStruct(..) |
ast::ItemConst(..) | ast::ItemStatic(..) | ast::ItemStruct(..) |
ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) |
ast::ItemMac(..) => {}
}
@ -1245,7 +1246,7 @@ impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> {
}
}
ast::ItemStatic(..) |
ast::ItemStatic(..) | ast::ItemConst(..) |
ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) |
ast::ItemMac(..) => {}
}

View File

@ -27,7 +27,6 @@ use syntax::abi;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{is_local, PostExpansionMethod};
use syntax::ast_util;
use syntax::attr::{InlineAlways, InlineHint, InlineNever, InlineNone};
use syntax::attr;
use syntax::visit::Visitor;
@ -121,15 +120,14 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ReachableContext<'a, 'tcx> {
self.worklist.push(def_id.node)
} else {
match def {
// If this path leads to a static, then we may have
// to do some work to figure out whether the static
// is indeed reachable. (Inlineable statics are
// never reachable.)
def::DefStatic(..) => {
// If this path leads to a constant, then we need to
// recurse into the constant to continue finding
// items that are reachable.
def::DefConst(..) => {
self.worklist.push(def_id.node);
}
// If this wasn't a static, then this destination is
// If this wasn't a static, then the destination is
// surely reachable.
_ => {
self.reachable_symbols.insert(def_id.node);
@ -238,15 +236,14 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
fn propagate(&mut self) {
let mut scanned = HashSet::new();
loop {
if self.worklist.len() == 0 {
break
}
let search_item = self.worklist.pop().unwrap();
if scanned.contains(&search_item) {
let search_item = match self.worklist.pop() {
Some(item) => item,
None => break,
};
if !scanned.insert(search_item) {
continue
}
scanned.insert(search_item);
match self.tcx.map.find(search_item) {
Some(ref item) => self.propagate_node(item, search_item),
None if search_item == ast::CRATE_NODE_ID => {}
@ -297,21 +294,17 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
}
}
// Statics with insignificant addresses are not reachable
// because they're inlined specially into all other crates.
ast::ItemStatic(_, mutbl, ref init) => {
if !ast_util::static_has_significant_address(
mutbl,
item.attrs.as_slice()) {
self.reachable_symbols.remove(&search_item);
}
visit::walk_expr(self, &**init);
// Reachable constants will be inlined into other crates
// unconditionally, so we need to make sure that their
// contents are also reachable.
ast::ItemConst(_, ref init) => {
self.visit_expr(&**init);
}
// These are normal, nothing reachable about these
// inherently and their children are already in the
// worklist, as determined by the privacy pass
ast::ItemTy(..) |
ast::ItemTy(..) | ast::ItemStatic(_, _, _) |
ast::ItemMod(..) | ast::ItemForeignMod(..) |
ast::ItemImpl(..) | ast::ItemTrait(..) |
ast::ItemStruct(..) | ast::ItemEnum(..) => {}

View File

@ -29,7 +29,7 @@ use syntax::ast::{ExprPath, ExprProc, ExprStruct, ExprUnboxedFn, FnDecl};
use syntax::ast::{ForeignItem, ForeignItemFn, ForeignItemStatic, Generics};
use syntax::ast::{Ident, ImplItem, Item, ItemEnum, ItemFn, ItemForeignMod};
use syntax::ast::{ItemImpl, ItemMac, ItemMod, ItemStatic, ItemStruct};
use syntax::ast::{ItemTrait, ItemTy, LOCAL_CRATE, Local};
use syntax::ast::{ItemTrait, ItemTy, LOCAL_CRATE, Local, ItemConst};
use syntax::ast::{MethodImplItem, Mod, Name, NamedField, NodeId};
use syntax::ast::{Pat, PatEnum, PatIdent, PatLit};
use syntax::ast::{PatRange, PatStruct, Path, PathListIdent, PathListMod};
@ -1243,6 +1243,12 @@ impl<'a> Resolver<'a> {
(DefStatic(local_def(item.id), mutbl), sp, is_public);
parent
}
ItemConst(_, _) => {
self.add_child(ident, parent.clone(), ForbidDuplicateValues, sp)
.define_value(DefConst(local_def(item.id)),
sp, is_public);
parent
}
ItemFn(_, fn_style, _, _, _) => {
let name_bindings =
self.add_child(ident, parent.clone(), ForbidDuplicateValues, sp);
@ -1829,7 +1835,7 @@ impl<'a> Resolver<'a> {
csearch::get_tuple_struct_definition_if_ctor(&self.session.cstore, ctor_id)
.map_or(def, |_| DefStruct(ctor_id)), DUMMY_SP, is_public);
}
DefFn(..) | DefStaticMethod(..) | DefStatic(..) => {
DefFn(..) | DefStaticMethod(..) | DefStatic(..) | DefConst(..) => {
debug!("(building reduced graph for external \
crate) building value (fn/static) {}", final_ident);
child_name_bindings.define_value(def, DUMMY_SP, is_public);
@ -4216,7 +4222,7 @@ impl<'a> Resolver<'a> {
&**block);
}
ItemStatic(..) => {
ItemConst(..) | ItemStatic(..) => {
self.with_constant_rib(|this| {
visit::walk_item(this, item);
});
@ -5084,6 +5090,7 @@ impl<'a> Resolver<'a> {
Some(def @ (DefFn(..), _)) |
Some(def @ (DefVariant(..), _)) |
Some(def @ (DefStruct(..), _)) |
Some(def @ (DefConst(..), _)) |
Some(def @ (DefStatic(..), _)) => {
self.record_def(pattern.id, def);
}
@ -5171,12 +5178,14 @@ impl<'a> Resolver<'a> {
def @ DefVariant(..) | def @ DefStruct(..) => {
return FoundStructOrEnumVariant(def, LastMod(AllPublic));
}
def @ DefStatic(_, false) => {
def @ DefConst(..) => {
return FoundConst(def, LastMod(AllPublic));
}
DefStatic(_, true) => {
DefStatic(..) => {
self.resolve_error(span,
"mutable static variables cannot be referenced in a pattern");
"static variables cannot be \
referenced in a pattern, \
use a `const` instead");
return BareIdentifierPatternUnresolved;
}
_ => {

View File

@ -92,7 +92,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
ast::ItemMod(..) |
ast::ItemMac(..) |
ast::ItemForeignMod(..) |
ast::ItemStatic(..) => {
ast::ItemStatic(..) | ast::ItemConst(..) => {
self.with(|_, f| f(RootScope), |v| visit::walk_item(v, item));
return;
}

View File

@ -230,6 +230,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
def::DefAssociatedTy(..) |
def::DefTrait(_) => Some(recorder::TypeRef),
def::DefStatic(_, _) |
def::DefConst(_) |
def::DefLocal(_) |
def::DefVariant(_, _, _) |
def::DefUpvar(..) => Some(recorder::VarRef),
@ -521,6 +522,29 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
self.visit_expr(expr);
}
fn process_const(&mut self,
item: &ast::Item,
typ: &ast::Ty,
expr: &ast::Expr)
{
let qualname = self.analysis.ty_cx.map.path_to_string(item.id);
let sub_span = self.span.sub_span_after_keyword(item.span,
keywords::Const);
self.fmt.static_str(item.span,
sub_span,
item.id,
get_ident(item.ident).get(),
qualname.as_slice(),
"",
ty_to_string(&*typ).as_slice(),
self.cur_scope);
// walk type and init value
self.visit_ty(&*typ);
self.visit_expr(expr);
}
fn process_struct(&mut self,
item: &ast::Item,
def: &ast::StructDef,
@ -740,6 +764,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
def::DefUpvar(..) |
def::DefLocal(..) |
def::DefStatic(..) |
def::DefConst(..) |
def::DefVariant(..) => self.fmt.ref_str(recorder::VarRef,
ex.span,
sub_span,
@ -807,6 +832,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
},
def::DefLocal(_) |
def::DefStatic(_,_) |
def::DefConst(..) |
def::DefStruct(_) |
def::DefFn(..) => self.write_sub_paths_truncated(path),
_ => {},
@ -1008,6 +1034,8 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
self.process_fn(item, &**decl, ty_params, &**body),
ast::ItemStatic(ref typ, mt, ref expr) =>
self.process_static(item, &**typ, mt, &**expr),
ast::ItemConst(ref typ, ref expr) =>
self.process_const(item, &**typ, &**expr),
ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params),
ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params),
ast::ItemImpl(ref ty_params,
@ -1386,6 +1414,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
self.cur_scope),
// FIXME(nrc) what is this doing here?
def::DefStatic(_, _) => {}
def::DefConst(..) => {}
_ => error!("unexpected definition kind when processing collected paths: {:?}",
*def)
}

View File

View File

@ -271,14 +271,14 @@ impl<'a> Opt<'a> {
match *self {
ConstantValue(ConstantExpr(lit_expr)) => {
let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_expr.id);
let (llval, _, _) = consts::const_expr(ccx, &*lit_expr, true);
let (llval, _) = consts::const_expr(ccx, &*lit_expr);
let lit_datum = immediate_rvalue(llval, lit_ty);
let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
SingleResult(Result::new(bcx, lit_datum.val))
}
ConstantRange(ConstantExpr(ref l1), ConstantExpr(ref l2)) => {
let (l1, _, _) = consts::const_expr(ccx, &**l1, true);
let (l2, _, _) = consts::const_expr(ccx, &**l2, true);
let (l1, _) = consts::const_expr(ccx, &**l1);
let (l2, _) = consts::const_expr(ccx, &**l2);
RangeResult(Result::new(bcx, l1), Result::new(bcx, l2))
}
Variant(disr_val, ref repr, _) => {
@ -350,7 +350,20 @@ struct ArmData<'p, 'blk, 'tcx: 'blk> {
struct Match<'a, 'p: 'a, 'blk: 'a, 'tcx: 'blk> {
pats: Vec<&'p ast::Pat>,
data: &'a ArmData<'p, 'blk, 'tcx>,
bound_ptrs: Vec<(Ident, ValueRef)>
bound_ptrs: Vec<(Ident, ValueRef)>,
// This is a pointer to an instance of check_match::DUMMY_WILD_PAT. The
// check_match code requires that we pass this in (with the same lifetime as
// the patterns passed in). Unfortunately this is required to be propagated
// into this structure in order to get the lifetimes to work.
//
// Lots of the `check_match` code will deal with &DUMMY_WILD_PAT when
// returning references, which used to have the `'static` lifetime before
// const was added to the language. The DUMMY_WILD_PAT does not implement
// Sync, however, so it must be a const, which longer has a static lifetime,
// hence we're passing it in here. This certainly isn't crucial, and if it
// can be removed, please do!
dummy: &'p ast::Pat,
}
impl<'a, 'p, 'blk, 'tcx> Repr for Match<'a, 'p, 'blk, 'tcx> {
@ -403,21 +416,22 @@ fn expand_nested_bindings<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
*pats.get_mut(col) = pat;
Match {
pats: pats,
dummy: br.dummy,
data: &*br.data,
bound_ptrs: bound_ptrs
}
}).collect()
}
type EnterPatterns<'a> = <'p> |&[&'p ast::Pat]|: 'a -> Option<Vec<&'p ast::Pat>>;
type EnterPatterns<'a, 'p> = |&[&'p ast::Pat]|: 'a -> Option<Vec<&'p ast::Pat>>;
fn enter_match<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
dm: &DefMap,
m: &[Match<'a, 'p, 'blk, 'tcx>],
col: uint,
val: ValueRef,
e: EnterPatterns)
-> Vec<Match<'a, 'p, 'blk, 'tcx>> {
fn enter_match<'a, 'b, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
dm: &DefMap,
m: &[Match<'a, 'p, 'blk, 'tcx>],
col: uint,
val: ValueRef,
e: EnterPatterns<'b, 'p>)
-> Vec<Match<'a, 'p, 'blk, 'tcx>> {
debug!("enter_match(bcx={}, m={}, col={}, val={})",
bcx.to_str(),
m.repr(bcx.tcx()),
@ -450,6 +464,7 @@ fn enter_match<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
Match {
pats: pats,
dummy: br.dummy,
data: br.data,
bound_ptrs: bound_ptrs
}
@ -544,7 +559,8 @@ fn enter_opt<'a, 'p, 'blk, 'tcx>(
let mcx = check_match::MatchCheckCtxt { tcx: bcx.tcx() };
enter_match(bcx, dm, m, col, val, |pats|
check_match::specialize(&mcx, pats.as_slice(), &ctor, col, variant_size)
check_match::specialize(&mcx, pats.as_slice(), m[0].dummy, &ctor, col,
variant_size)
)
}
@ -1025,7 +1041,9 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
match adt_vals {
Some(field_vals) => {
let pats = enter_match(bcx, dm, m, col, val, |pats|
check_match::specialize(&mcx, pats, &check_match::Single, col, field_vals.len())
check_match::specialize(&mcx, pats, m[0].dummy,
&check_match::Single, col,
field_vals.len())
);
let vals = field_vals.append(vals_left.as_slice());
compile_submatch(bcx, pats.as_slice(), vals.as_slice(), chk, has_genuine_default);
@ -1347,6 +1365,7 @@ fn trans_match_inner<'blk, 'tcx>(scope_cx: Block<'blk, 'tcx>,
bindings_map: create_bindings_map(bcx, &**arm.pats.get(0), discr_expr, &*arm.body)
}).collect();
let dummy = check_match::DUMMY_WILD_PAT.clone();
let mut static_inliner = StaticInliner::new(scope_cx.tcx());
let arm_pats: Vec<Vec<P<ast::Pat>>> = arm_datas.iter().map(|arm_data| {
arm_data.arm.pats.iter().map(|p| static_inliner.fold_pat((*p).clone())).collect()
@ -1355,6 +1374,7 @@ fn trans_match_inner<'blk, 'tcx>(scope_cx: Block<'blk, 'tcx>,
for (arm_data, pats) in arm_datas.iter().zip(arm_pats.iter()) {
matches.extend(pats.iter().map(|p| Match {
pats: vec![&**p],
dummy: &dummy,
data: arm_data,
bound_ptrs: Vec::new(),
}));

View File

@ -32,7 +32,7 @@ use driver::config::{NoDebugInfo, FullDebugInfo};
use driver::driver::{CrateAnalysis, CrateTranslation, ModuleTranslation};
use driver::session::Session;
use lint;
use llvm::{BasicBlockRef, ModuleRef, ValueRef, Vector, get_param};
use llvm::{BasicBlockRef, ValueRef, Vector, get_param};
use llvm;
use metadata::{csearch, encoder, loader};
use middle::astencode;
@ -89,7 +89,7 @@ use std::rc::Rc;
use std::{i8, i16, i32, i64};
use syntax::abi::{X86, X86_64, Arm, Mips, Mipsel, Rust, RustCall};
use syntax::abi::{RustIntrinsic, Abi, OsWindows};
use syntax::ast_util::{local_def, is_local};
use syntax::ast_util::local_def;
use syntax::attr::AttrMetaMethods;
use syntax::attr;
use syntax::codemap::Span;
@ -317,17 +317,31 @@ pub fn decl_internal_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str) -> Va
llfn
}
pub fn get_extern_const(externs: &mut ExternMap, llmod: ModuleRef,
name: &str, ty: Type) -> ValueRef {
match externs.find_equiv(&name) {
pub fn get_extern_const(ccx: &CrateContext, did: ast::DefId,
t: ty::t) -> ValueRef {
let name = csearch::get_symbol(&ccx.sess().cstore, did);
let ty = type_of(ccx, t);
match ccx.externs().borrow_mut().find(&name) {
Some(n) => return *n,
None => ()
}
unsafe {
let c = name.with_c_str(|buf| {
llvm::LLVMAddGlobal(llmod, ty.to_ref(), buf)
llvm::LLVMAddGlobal(ccx.llmod(), ty.to_ref(), buf)
});
externs.insert(name.to_string(), c);
// Thread-local statics in some other crate need to *always* be linked
// against in a thread-local fashion, so we need to be sure to apply the
// thread-local attribute locally if it was present remotely. If we
// don't do this then linker errors can be generated where the linker
// complains that one object files has a thread local version of the
// symbol and another one doesn't.
ty::each_attr(ccx.tcx(), did, |attr| {
if attr.check_name("thread_local") {
llvm::set_thread_local(c, true);
}
true
});
ccx.externs().borrow_mut().insert(name.to_string(), c);
return c;
}
}
@ -935,11 +949,7 @@ pub fn trans_external_path(ccx: &CrateContext, did: ast::DefId, t: ty::t) -> Val
get_extern_rust_fn(ccx, t, name.as_slice(), did)
}
_ => {
let llty = type_of(ccx, t);
get_extern_const(&mut *ccx.externs().borrow_mut(),
ccx.llmod(),
name.as_slice(),
llty)
get_extern_const(ccx, did, t)
}
}
}
@ -2228,21 +2238,19 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
ast::ItemEnum(ref enum_definition, _) => {
enum_variant_size_lint(ccx, enum_definition, item.span, item.id);
}
ast::ItemConst(_, ref expr) => {
// Recurse on the expression to catch items in blocks
let mut v = TransItemVisitor{ ccx: ccx };
v.visit_expr(&**expr);
}
ast::ItemStatic(_, m, ref expr) => {
// Recurse on the expression to catch items in blocks
let mut v = TransItemVisitor{ ccx: ccx };
v.visit_expr(&**expr);
let trans_everywhere = attr::requests_inline(item.attrs.as_slice());
for (ref ccx, is_origin) in ccx.maybe_iter(!from_external && trans_everywhere) {
consts::trans_const(ccx, m, item.id);
let g = get_item_val(ccx, item.id);
update_linkage(ccx,
g,
Some(item.id),
if is_origin { OriginalTranslation } else { InlinedCopy });
}
consts::trans_static(ccx, m, item.id);
let g = get_item_val(ccx, item.id);
update_linkage(ccx, g, Some(item.id), OriginalTranslation);
// Do static_assert checking. It can't really be done much earlier
// because we need to get the value of the bool out of LLVM
@ -2253,7 +2261,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
static");
}
let v = ccx.const_values().borrow().get_copy(&item.id);
let v = ccx.static_values().borrow().get_copy(&item.id);
unsafe {
if !(llvm::LLVMConstIntGetZExtValue(v) != 0) {
ccx.sess().span_fatal(expr.span, "static assertion failed");
@ -2667,23 +2675,21 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
let val = match item {
ast_map::NodeItem(i) => {
let ty = ty::node_id_to_type(ccx.tcx(), i.id);
let sym = exported_name(ccx, id, ty, i.attrs.as_slice());
let sym = || exported_name(ccx, id, ty, i.attrs.as_slice());
let v = match i.node {
ast::ItemStatic(_, mutbl, ref expr) => {
ast::ItemStatic(_, _, ref expr) => {
// If this static came from an external crate, then
// we need to get the symbol from csearch instead of
// using the current crate's name/version
// information in the hash of the symbol
let sym = sym();
debug!("making {}", sym);
let is_local = !ccx.external_srcs().borrow().contains_key(&id);
// We need the translated value here, because for enums the
// LLVM type is not fully determined by the Rust type.
let (v, inlineable, ty) = consts::const_expr(ccx, &**expr, is_local);
ccx.const_values().borrow_mut().insert(id, v);
let mut inlineable = inlineable;
let (v, ty) = consts::const_expr(ccx, &**expr);
ccx.static_values().borrow_mut().insert(id, v);
unsafe {
// boolean SSA values are i1, but they have to be stored in i8 slots,
// otherwise some LLVM optimization passes don't work as expected
@ -2694,55 +2700,30 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
};
if contains_null(sym.as_slice()) {
ccx.sess().fatal(
format!("Illegal null byte in export_name value: `{}`",
sym).as_slice());
format!("Illegal null byte in export_name \
value: `{}`", sym).as_slice());
}
let g = sym.as_slice().with_c_str(|buf| {
llvm::LLVMAddGlobal(ccx.llmod(), llty, buf)
});
// Apply the `unnamed_addr` attribute if
// requested
if !ast_util::static_has_significant_address(
mutbl,
i.attrs.as_slice()) {
llvm::SetUnnamedAddr(g, true);
// This is a curious case where we must make
// all of these statics inlineable. If a
// global is not tagged as `#[inline(never)]`,
// then LLVM won't coalesce globals unless they
// have an internal linkage type. This means that
// external crates cannot use this global.
// This is a problem for things like inner
// statics in generic functions, because the
// function will be inlined into another
// crate and then attempt to link to the
// static in the original crate, only to
// find that it's not there. On the other
// side of inlining, the crates knows to
// not declare this static as
// available_externally (because it isn't)
inlineable = true;
}
if attr::contains_name(i.attrs.as_slice(),
"thread_local") {
llvm::set_thread_local(g, true);
}
if !inlineable {
debug!("{} not inlined", sym);
ccx.non_inlineable_statics().borrow_mut()
.insert(id);
}
ccx.item_symbols().borrow_mut().insert(i.id, sym);
g
}
}
ast::ItemConst(_, ref expr) => {
let (v, _) = consts::const_expr(ccx, &**expr);
ccx.const_values().borrow_mut().insert(id, v);
v
}
ast::ItemFn(_, _, abi, _, _) => {
let sym = sym();
let llfn = if abi == Rust {
register_fn(ccx, i.span, sym, i.id, ty)
} else {
@ -2911,7 +2892,6 @@ pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'tcx>,
tcx: cx.tcx(),
reexports2: cx.exp_map2(),
item_symbols: cx.item_symbols(),
non_inlineable_statics: cx.non_inlineable_statics(),
link_meta: cx.link_meta(),
cstore: &cx.sess().cstore,
encode_inlined_item: ie,

View File

@ -196,6 +196,7 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr)
}
}
def::DefStatic(..) |
def::DefConst(..) |
def::DefLocal(..) |
def::DefUpvar(..) => {
datum_callee(bcx, ref_expr)

View File

@ -33,7 +33,6 @@ use middle::ty;
use util::ppaux::{Repr, ty_to_string};
use std::c_str::ToCStr;
use std::vec;
use libc::c_uint;
use syntax::{ast, ast_util};
use syntax::ptr::P;
@ -96,24 +95,20 @@ pub fn const_ptrcast(cx: &CrateContext, a: ValueRef, t: Type) -> ValueRef {
}
}
// Helper function because we don't have tuple-swizzling.
fn first_two<R, S, T>((a, b, _): (R, S, T)) -> (R, S) {
(a, b)
}
fn const_vec(cx: &CrateContext, e: &ast::Expr,
es: &[P<ast::Expr>], is_local: bool) -> (ValueRef, Type, bool) {
es: &[P<ast::Expr>]) -> (ValueRef, Type) {
let vec_ty = ty::expr_ty(cx.tcx(), e);
let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty);
let llunitty = type_of::type_of(cx, unit_ty);
let (vs, inlineable) = vec::unzip(es.iter().map(|e| first_two(const_expr(cx, &**e, is_local))));
let vs = es.iter().map(|e| const_expr(cx, &**e).val0())
.collect::<Vec<_>>();
// If the vector contains enums, an LLVM array won't work.
let v = if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
C_struct(cx, vs.as_slice(), false)
} else {
C_array(llunitty, vs.as_slice())
};
(v, llunitty, inlineable.iter().fold(true, |a, &b| a && b))
(v, llunitty)
}
pub fn const_addr_of(cx: &CrateContext, cv: ValueRef, mutbl: ast::Mutability) -> ValueRef {
@ -177,7 +172,7 @@ fn const_deref(cx: &CrateContext, v: ValueRef, t: ty::t, explicit: bool)
}
pub fn get_const_val(cx: &CrateContext,
mut def_id: ast::DefId) -> (ValueRef, bool) {
mut def_id: ast::DefId) -> ValueRef {
let contains_key = cx.const_values().borrow().contains_key(&def_id.node);
if !ast_util::is_local(def_id) || !contains_key {
if !ast_util::is_local(def_id) {
@ -185,21 +180,17 @@ pub fn get_const_val(cx: &CrateContext,
}
match cx.tcx().map.expect_item(def_id.node).node {
ast::ItemStatic(_, ast::MutImmutable, _) => {
trans_const(cx, ast::MutImmutable, def_id.node);
}
ast::ItemConst(..) => { base::get_item_val(cx, def_id.node); }
_ => {}
}
}
(cx.const_values().borrow().get_copy(&def_id.node),
!cx.non_inlineable_statics().borrow().contains(&def_id.node))
cx.const_values().borrow().get_copy(&def_id.node)
}
pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef, bool, ty::t) {
let (llconst, inlineable) = const_expr_unadjusted(cx, e, is_local);
pub fn const_expr(cx: &CrateContext, e: &ast::Expr) -> (ValueRef, ty::t) {
let llconst = const_expr_unadjusted(cx, e);
let mut llconst = llconst;
let mut inlineable = inlineable;
let ety = ty::expr_ty(cx.tcx(), e);
let mut ety_adjusted = ty::expr_ty_adjusted(cx.tcx(), e);
let opt_adj = cx.tcx().adjustments.borrow().find_copy(&e.id);
@ -213,7 +204,7 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef
ety_adjusted,
def,
llconst,
is_local);
true);
llconst = C_struct(cx, [wrapper, C_null(Type::i8p(cx))], false)
}
ty::AdjustAddEnv(store) => {
@ -250,7 +241,6 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef
// Don't copy data to do a deref+ref
// (i.e., skip the last auto-deref).
if adj.autoderefs == 0 {
inlineable = false;
llconst = const_addr_of(cx, llconst, ast::MutImmutable);
}
}
@ -271,7 +261,6 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef
match ty::get(ty).sty {
ty::ty_vec(unit_ty, Some(len)) => {
inlineable = false;
let llunitty = type_of::type_of(cx, unit_ty);
let llptr = const_ptrcast(cx, llconst, llunitty);
assert_eq!(abi::slice_elt_base, 0);
@ -314,29 +303,25 @@ pub fn const_expr(cx: &CrateContext, e: &ast::Expr, is_local: bool) -> (ValueRef
e.repr(cx.tcx()), ty_to_string(cx.tcx(), ety),
csize, tsize).as_slice());
}
(llconst, inlineable, ety_adjusted)
(llconst, ety_adjusted)
}
// the bool returned is whether this expression can be inlined into other crates
// if it's assigned to a static.
fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
is_local: bool) -> (ValueRef, bool) {
fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr) -> ValueRef {
let map_list = |exprs: &[P<ast::Expr>]| {
exprs.iter().map(|e| first_two(const_expr(cx, &**e, is_local)))
.fold((Vec::new(), true),
|(l, all_inlineable), (val, inlineable)| {
(l.append_one(val), all_inlineable && inlineable)
})
exprs.iter().map(|e| const_expr(cx, &**e).val0())
.fold(Vec::new(), |l, val| l.append_one(val))
};
unsafe {
let _icx = push_ctxt("const_expr");
return match e.node {
ast::ExprLit(ref lit) => {
(consts::const_lit(cx, e, &**lit), true)
consts::const_lit(cx, e, &**lit)
}
ast::ExprBinary(b, ref e1, ref e2) => {
let (te1, _, _) = const_expr(cx, &**e1, is_local);
let (te2, _, _) = const_expr(cx, &**e2, is_local);
let (te1, _) = const_expr(cx, &**e1);
let (te2, _) = const_expr(cx, &**e2);
let te2 = base::cast_shift_const_rhs(b, te1, te2);
@ -345,7 +330,7 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
let ty = ty::expr_ty(cx.tcx(), &**e1);
let is_float = ty::type_is_fp(ty);
let signed = ty::type_is_signed(ty);
return (match b {
return match b {
ast::BiAdd => {
if is_float { llvm::LLVMConstFAdd(te1, te2) }
else { llvm::LLVMConstAdd(te1, te2) }
@ -414,13 +399,13 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
else { ConstICmp(IntUGT, te1, te2) }
}
},
}, true)
}
},
ast::ExprUnary(u, ref e) => {
let (te, _, _) = const_expr(cx, &**e, is_local);
let (te, _) = const_expr(cx, &**e);
let ty = ty::expr_ty(cx.tcx(), &**e);
let is_float = ty::type_is_fp(ty);
return (match u {
return match u {
ast::UnUniq | ast::UnDeref => {
let (dv, _dt) = const_deref(cx, te, ty, true);
dv
@ -430,26 +415,26 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
if is_float { llvm::LLVMConstFNeg(te) }
else { llvm::LLVMConstNeg(te) }
}
}, true)
}
}
ast::ExprField(ref base, field, _) => {
let (bv, inlineable, bt) = const_expr(cx, &**base, is_local);
let (bv, bt) = const_expr(cx, &**base);
let brepr = adt::represent_type(cx, bt);
expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| {
let ix = ty::field_idx_strict(cx.tcx(), field.node.name, field_tys);
(adt::const_get_field(cx, &*brepr, bv, discr, ix), inlineable)
adt::const_get_field(cx, &*brepr, bv, discr, ix)
})
}
ast::ExprTupField(ref base, idx, _) => {
let (bv, inlineable, bt) = const_expr(cx, &**base, is_local);
let (bv, bt) = const_expr(cx, &**base);
let brepr = adt::represent_type(cx, bt);
expr::with_field_tys(cx.tcx(), bt, None, |discr, _| {
(adt::const_get_field(cx, &*brepr, bv, discr, idx.node), inlineable)
adt::const_get_field(cx, &*brepr, bv, discr, idx.node)
})
}
ast::ExprIndex(ref base, ref index) => {
let (bv, inlineable, bt) = const_expr(cx, &**base, is_local);
let (bv, bt) = const_expr(cx, &**base);
let iv = match const_eval::eval_const_expr(cx.tcx(), &**index) {
const_eval::const_int(i) => i as u64,
const_eval::const_uint(u) => u,
@ -500,13 +485,13 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
cx.sess().span_err(e.span,
"const index-expr is out of bounds");
}
(const_get_elt(cx, arr, [iv as c_uint]), inlineable)
const_get_elt(cx, arr, [iv as c_uint])
}
ast::ExprCast(ref base, _) => {
let ety = ty::expr_ty(cx.tcx(), e);
let llty = type_of::type_of(cx, ety);
let (v, inlineable, basety) = const_expr(cx, &**base, is_local);
return (match (expr::cast_type_kind(cx.tcx(), basety),
let (v, basety) = const_expr(cx, &**base);
return match (expr::cast_type_kind(cx.tcx(), basety),
expr::cast_type_kind(cx.tcx(), ety)) {
(expr::cast_integral, expr::cast_integral) => {
@ -554,17 +539,38 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
cx.sess().impossible_case(e.span,
"bad combination of types for cast")
}
}, inlineable)
}
}
ast::ExprAddrOf(mutbl, ref sub) => {
let (e, _, _) = const_expr(cx, &**sub, is_local);
(const_addr_of(cx, e, mutbl), false)
// If this is the address of some static, then we need to return
// the actual address of the static itself (short circuit the rest
// of const eval).
let mut cur = sub;
loop {
match cur.node {
ast::ExprParen(ref sub) => cur = sub,
_ => break,
}
}
let opt_def = cx.tcx().def_map.borrow().find_copy(&cur.id);
match opt_def {
Some(def::DefStatic(def_id, _)) => {
let ty = ty::expr_ty(cx.tcx(), e);
return get_static_val(cx, def_id, ty);
}
_ => {}
}
// If this isn't the address of a static, then keep going through
// normal constant evaluation.
let (e, _) = const_expr(cx, &**sub);
const_addr_of(cx, e, mutbl)
}
ast::ExprTup(ref es) => {
let ety = ty::expr_ty(cx.tcx(), e);
let repr = adt::represent_type(cx, ety);
let (vals, inlineable) = map_list(es.as_slice());
(adt::trans_const(cx, &*repr, 0, vals.as_slice()), inlineable)
let vals = map_list(es.as_slice());
adt::trans_const(cx, &*repr, 0, vals.as_slice())
}
ast::ExprStruct(_, ref fs, ref base_opt) => {
let ety = ty::expr_ty(cx.tcx(), e);
@ -572,36 +578,34 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
let tcx = cx.tcx();
let base_val = match *base_opt {
Some(ref base) => Some(const_expr(cx, &**base, is_local)),
Some(ref base) => Some(const_expr(cx, &**base)),
None => None
};
expr::with_field_tys(tcx, ety, Some(e.id), |discr, field_tys| {
let (cs, inlineable) = vec::unzip(field_tys.iter().enumerate()
.map(|(ix, &field_ty)| {
let cs = field_tys.iter().enumerate()
.map(|(ix, &field_ty)| {
match fs.iter().find(|f| field_ty.ident.name == f.ident.node.name) {
Some(ref f) => first_two(const_expr(cx, &*f.expr, is_local)),
Some(ref f) => const_expr(cx, &*f.expr).val0(),
None => {
match base_val {
Some((bv, inlineable, _)) => {
(adt::const_get_field(cx, &*repr, bv, discr, ix),
inlineable)
}
None => cx.sess().span_bug(e.span, "missing struct field")
Some((bv, _)) => {
adt::const_get_field(cx, &*repr, bv,
discr, ix)
}
None => {
cx.sess().span_bug(e.span,
"missing struct field")
}
}
}
}
}));
(adt::trans_const(cx, &*repr, discr, cs.as_slice()),
inlineable.iter().fold(true, |a, &b| a && b))
}).collect::<Vec<_>>();
adt::trans_const(cx, &*repr, discr, cs.as_slice())
})
}
ast::ExprVec(ref es) => {
let (v, _, inlineable) = const_vec(cx,
e,
es.as_slice(),
is_local);
(v, inlineable)
const_vec(cx, e, es.as_slice()).val0()
}
ast::ExprRepeat(ref elem, ref count) => {
let vec_ty = ty::expr_ty(cx.tcx(), e);
@ -612,13 +616,12 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
const_eval::const_uint(i) => i as uint,
_ => cx.sess().span_bug(count.span, "count must be integral const expression.")
};
let vs = Vec::from_elem(n, const_expr(cx, &**elem, is_local).val0());
let v = if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
let vs = Vec::from_elem(n, const_expr(cx, &**elem).val0());
if vs.iter().any(|vi| val_ty(*vi) != llunitty) {
C_struct(cx, vs.as_slice(), false)
} else {
C_array(llunitty, vs.as_slice())
};
(v, true)
}
}
ast::ExprPath(ref pth) => {
// Assert that there are no type parameters in this path.
@ -629,13 +632,13 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
Some(def::DefFn(def_id, _fn_style, _)) => {
if !ast_util::is_local(def_id) {
let ty = csearch::get_type(cx.tcx(), def_id).ty;
(base::trans_external_path(cx, def_id, ty), true)
base::trans_external_path(cx, def_id, ty)
} else {
assert!(ast_util::is_local(def_id));
(base::get_item_val(cx, def_id.node), true)
base::get_item_val(cx, def_id.node)
}
}
Some(def::DefStatic(def_id, false)) => {
Some(def::DefConst(def_id)) => {
get_const_val(cx, def_id)
}
Some(def::DefVariant(enum_did, variant_did, _)) => {
@ -644,15 +647,16 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
let vinfo = ty::enum_variant_with_id(cx.tcx(),
enum_did,
variant_did);
(adt::trans_const(cx, &*repr, vinfo.disr_val, []), true)
adt::trans_const(cx, &*repr, vinfo.disr_val, [])
}
Some(def::DefStruct(_)) => {
let ety = ty::expr_ty(cx.tcx(), e);
let llty = type_of::type_of(cx, ety);
(C_null(llty), true)
C_null(llty)
}
_ => {
cx.sess().span_bug(e.span, "expected a const, fn, struct, or variant def")
cx.sess().span_bug(e.span, "expected a const, fn, struct, \
or variant def")
}
}
}
@ -662,9 +666,8 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
Some(def::DefStruct(_)) => {
let ety = ty::expr_ty(cx.tcx(), e);
let repr = adt::represent_type(cx, ety);
let (arg_vals, inlineable) = map_list(args.as_slice());
(adt::trans_const(cx, &*repr, 0, arg_vals.as_slice()),
inlineable)
let arg_vals = map_list(args.as_slice());
adt::trans_const(cx, &*repr, 0, arg_vals.as_slice())
}
Some(def::DefVariant(enum_did, variant_did, _)) => {
let ety = ty::expr_ty(cx.tcx(), e);
@ -672,20 +675,20 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
let vinfo = ty::enum_variant_with_id(cx.tcx(),
enum_did,
variant_did);
let (arg_vals, inlineable) = map_list(args.as_slice());
(adt::trans_const(cx,
&*repr,
vinfo.disr_val,
arg_vals.as_slice()), inlineable)
let arg_vals = map_list(args.as_slice());
adt::trans_const(cx,
&*repr,
vinfo.disr_val,
arg_vals.as_slice())
}
_ => cx.sess().span_bug(e.span, "expected a struct or variant def")
}
}
ast::ExprParen(ref e) => first_two(const_expr(cx, &**e, is_local)),
ast::ExprParen(ref e) => const_expr(cx, &**e).val0(),
ast::ExprBlock(ref block) => {
match block.expr {
Some(ref expr) => first_two(const_expr(cx, &**expr, is_local)),
None => (C_nil(cx), true)
Some(ref expr) => const_expr(cx, &**expr).val0(),
None => C_nil(cx)
}
}
_ => cx.sess().span_bug(e.span,
@ -694,13 +697,13 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr,
}
}
pub fn trans_const(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) {
pub fn trans_static(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) {
unsafe {
let _icx = push_ctxt("trans_const");
let _icx = push_ctxt("trans_static");
let g = base::get_item_val(ccx, id);
// At this point, get_item_val has already translated the
// constant's initializer to determine its LLVM type.
let v = ccx.const_values().borrow().get_copy(&id);
let v = ccx.static_values().borrow().get_copy(&id);
// boolean SSA values are i1, but they have to be stored in i8 slots,
// otherwise some LLVM optimization passes don't work as expected
let v = if llvm::LLVMTypeOf(v) == Type::i1(ccx).to_ref() {
@ -710,17 +713,20 @@ pub fn trans_const(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) {
};
llvm::LLVMSetInitializer(g, v);
// `get_item_val` left `g` with external linkage, but we just set an
// initializer for it. But we don't know yet if `g` should really be
// defined in this compilation unit, so we set its linkage to
// `AvailableExternallyLinkage`. (It's still a definition, but acts
// like a declaration for most purposes.) If `g` really should be
// declared here, then `trans_item` will fix up the linkage later on.
llvm::SetLinkage(g, llvm::AvailableExternallyLinkage);
// As an optimization, all shared statics which do not have interior
// mutability are placed into read-only memory.
if m != ast::MutMutable {
llvm::LLVMSetGlobalConstant(g, True);
let node_ty = ty::node_id_to_type(ccx.tcx(), id);
let tcontents = ty::type_contents(ccx.tcx(), node_ty);
if !tcontents.interior_unsafe() {
llvm::LLVMSetGlobalConstant(g, True);
}
}
debuginfo::create_global_var_metadata(ccx, id, g);
}
}
fn get_static_val(ccx: &CrateContext, did: ast::DefId, ty: ty::t) -> ValueRef {
if ast_util::is_local(did) { return base::get_item_val(ccx, did.node) }
base::trans_external_path(ccx, did, ty)
}

View File

@ -66,10 +66,6 @@ pub struct SharedCrateContext<'tcx> {
reachable: NodeSet,
item_symbols: RefCell<NodeMap<String>>,
link_meta: LinkMeta,
/// A set of static items which cannot be inlined into other crates. This
/// will prevent in IIItem() structures from being encoded into the metadata
/// that is generated
non_inlineable_statics: RefCell<NodeSet>,
symbol_hasher: RefCell<Sha256>,
tcx: ty::ctxt<'tcx>,
stats: Stats,
@ -121,6 +117,9 @@ pub struct LocalCrateContext {
/// Cache of emitted const values
const_values: RefCell<NodeMap<ValueRef>>,
/// Cache of emitted static values
static_values: RefCell<NodeMap<ValueRef>>,
/// Cache of external const values
extern_const_values: RefCell<DefIdMap<ValueRef>>,
@ -259,7 +258,6 @@ impl<'tcx> SharedCrateContext<'tcx> {
reachable: reachable,
item_symbols: RefCell::new(NodeMap::new()),
link_meta: link_meta,
non_inlineable_statics: RefCell::new(NodeSet::new()),
symbol_hasher: RefCell::new(symbol_hasher),
tcx: tcx,
stats: Stats {
@ -351,10 +349,6 @@ impl<'tcx> SharedCrateContext<'tcx> {
&self.link_meta
}
pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell<NodeSet> {
&self.non_inlineable_statics
}
pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell<Sha256> {
&self.symbol_hasher
}
@ -414,6 +408,7 @@ impl LocalCrateContext {
const_cstr_cache: RefCell::new(HashMap::new()),
const_globals: RefCell::new(HashMap::new()),
const_values: RefCell::new(NodeMap::new()),
static_values: RefCell::new(NodeMap::new()),
extern_const_values: RefCell::new(DefIdMap::new()),
impl_method_cache: RefCell::new(HashMap::new()),
closure_bare_wrapper_cache: RefCell::new(HashMap::new()),
@ -610,10 +605,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.local.external_srcs
}
pub fn non_inlineable_statics<'a>(&'a self) -> &'a RefCell<NodeSet> {
&self.shared.non_inlineable_statics
}
pub fn monomorphized<'a>(&'a self) -> &'a RefCell<HashMap<MonoId, ValueRef>> {
&self.local.monomorphized
}
@ -638,6 +629,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
&self.local.const_values
}
pub fn static_values<'a>(&'a self) -> &'a RefCell<NodeMap<ValueRef>> {
&self.local.static_values
}
pub fn extern_const_values<'a>(&'a self) -> &'a RefCell<DefIdMap<ValueRef>> {
&self.local.extern_const_values
}

View File

@ -776,6 +776,7 @@ pub fn create_global_var_metadata(cx: &CrateContext,
ast_map::NodeItem(item) => {
match item.node {
ast::ItemStatic(..) => (item.ident, item.span),
ast::ItemConst(..) => (item.ident, item.span),
_ => {
cx.sess()
.span_bug(item.span,

View File

@ -36,7 +36,6 @@
use back::abi;
use llvm;
use llvm::{ValueRef};
use metadata::csearch;
use middle::def;
use middle::mem_categorization::Typer;
use middle::subst;
@ -839,25 +838,20 @@ fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
trans_def_fn_unadjusted(bcx, ref_expr, def)
}
def::DefStatic(did, _) => {
// There are three things that may happen here:
// There are two things that may happen here:
// 1) If the static item is defined in this crate, it will be
// translated using `get_item_val`, and we return a pointer to
// the result.
// 2) If the static item is defined in another crate, but is
// marked inlineable, then it will be inlined into this crate
// and then translated with `get_item_val`. Again, we return a
// pointer to the result.
// 3) If the static item is defined in another crate and is not
// marked inlineable, then we add (or reuse) a declaration of
// an external global, and return a pointer to that.
// 2) If the static item is defined in another crate then we add
// (or reuse) a declaration of an external global, and return a
// pointer to that.
let const_ty = expr_ty(bcx, ref_expr);
fn get_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, did: ast::DefId, const_ty: ty::t)
-> ValueRef {
fn get_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, did: ast::DefId,
const_ty: ty::t) -> ValueRef {
// For external constants, we don't inline.
if did.krate == ast::LOCAL_CRATE {
// Case 1 or 2. (The inlining in case 2 produces a new
// DefId in LOCAL_CRATE.)
// Case 1.
// The LLVM global has the type of its initializer,
// which may not be equal to the enum's type for
@ -866,36 +860,41 @@ fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to();
PointerCast(bcx, val, pty)
} else {
// Case 3.
match bcx.ccx().extern_const_values().borrow().find(&did) {
None => {} // Continue.
Some(llval) => {
return *llval;
}
}
unsafe {
let llty = type_of::type_of(bcx.ccx(), const_ty);
let symbol = csearch::get_symbol(
&bcx.ccx().sess().cstore,
did);
let llval = symbol.as_slice().with_c_str(|buf| {
llvm::LLVMAddGlobal(bcx.ccx().llmod(),
llty.to_ref(),
buf)
});
bcx.ccx().extern_const_values().borrow_mut()
.insert(did, llval);
llval
}
// Case 2.
base::get_extern_const(bcx.ccx(), did, const_ty)
}
}
// The DefId produced by `maybe_instantiate_inline`
// may be in the LOCAL_CRATE or not.
let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
let val = get_val(bcx, did, const_ty);
DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr))
}
def::DefConst(did) => {
// First, inline any external constants into the local crate so we
// can be sure to get the LLVM value corresponding to it.
let did = inline::maybe_instantiate_inline(bcx.ccx(), did);
if did.krate != ast::LOCAL_CRATE {
bcx.tcx().sess.span_bug(ref_expr.span,
"cross crate constant could not \
be inlined");
}
let val = base::get_item_val(bcx.ccx(), did.node);
// Next, we need to crate a ByRef rvalue datum to return. We can't
// use the normal .to_ref_datum() function because the type of
// `val` is not actually the same as `const_ty`.
//
// To get around this, we make a custom alloca slot with the
// appropriate type (const_ty), and then we cast it to a pointer of
// typeof(val), store the value, and then hand this slot over to
// the datum infrastructure.
let const_ty = expr_ty(bcx, ref_expr);
let llty = type_of::type_of(bcx.ccx(), const_ty);
let slot = alloca(bcx, llty, "const");
let pty = Type::from_ref(unsafe { llvm::LLVMTypeOf(val) }).ptr_to();
Store(bcx, val, PointerCast(bcx, slot, pty));
let datum = Datum::new(slot, const_ty, Rvalue::new(ByRef));
DatumBlock::new(bcx, datum.to_expr_datum())
}
_ => {
DatumBlock::new(bcx, trans_local_var(bcx, def).to_expr_datum())
}

View File

@ -17,7 +17,6 @@ use middle::ty;
use syntax::ast;
use syntax::ast_util::{local_def, PostExpansionMethod};
use syntax::ast_util;
fn instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
-> Option<ast::DefId> {
@ -76,21 +75,7 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
}
}
}
ast::ItemStatic(_, mutbl, _) => {
if !ast_util::static_has_significant_address(mutbl, item.attrs.as_slice()) {
// Inlined static items use internal linkage when
// possible, so that LLVM will coalesce globals with
// identical initializers. (It only does this for
// globals with unnamed_addr and either internal or
// private linkage.)
Some(InternalLinkage)
} else {
// The address is significant, so we can't create an
// internal copy of the static. (The copy would have a
// different address from the original.)
Some(AvailableExternallyLinkage)
}
}
ast::ItemConst(..) => None,
_ => unreachable!(),
};

View File

@ -1374,6 +1374,7 @@ impl ParameterEnvironment {
ast::ItemEnum(..) |
ast::ItemStruct(..) |
ast::ItemImpl(..) |
ast::ItemConst(..) |
ast::ItemStatic(..) => {
let def_id = ast_util::local_def(id);
let pty = ty::lookup_item_type(cx, def_id);
@ -3576,6 +3577,8 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
def::DefUpvar(..) |
def::DefLocal(..) => LvalueExpr,
def::DefConst(..) => RvalueDatumExpr,
def => {
tcx.sess.span_bug(
expr.span,

View File

@ -677,7 +677,8 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
let _indenter = indenter();
match it.node {
ast::ItemStatic(_, _, ref e) => check_const(ccx, it.span, &**e, it.id),
ast::ItemStatic(_, _, ref e) |
ast::ItemConst(_, ref e) => check_const(ccx, it.span, &**e, it.id),
ast::ItemEnum(ref enum_definition, _) => {
check_enum_variants(ccx,
it.span,
@ -5083,7 +5084,7 @@ pub fn polytype_for_def(fcx: &FnCtxt,
}
def::DefFn(id, _, _) | def::DefStaticMethod(id, _, _) |
def::DefStatic(id, _) | def::DefVariant(_, id, _) |
def::DefStruct(id) => {
def::DefStruct(id) | def::DefConst(id) => {
return ty::lookup_item_type(fcx.ccx.tcx, id);
}
def::DefTrait(_) |
@ -5211,6 +5212,7 @@ pub fn instantiate_path(fcx: &FnCtxt,
// Case 2. Reference to a top-level value.
def::DefFn(..) |
def::DefConst(..) |
def::DefStatic(..) => {
segment_spaces = Vec::from_elem(path.segments.len() - 1, None);
segment_spaces.push(Some(subst::FnSpace));

View File

@ -69,6 +69,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
ast::ItemStatic(..) => {
self.check_item_type(item);
}
ast::ItemConst(..) => {
self.check_item_type(item);
}
ast::ItemStruct(ref struct_def, _) => {
self.check_type_defn(item, |fcx| {
vec![struct_variant(fcx, &**struct_def)]

View File

@ -1550,7 +1550,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item)
_ => {}
}
match it.node {
ast::ItemStatic(ref t, _, _) => {
ast::ItemStatic(ref t, _, _) | ast::ItemConst(ref t, _) => {
let typ = ccx.to_ty(&ExplicitRscope, &**t);
let pty = no_params(typ);

View File

@ -197,7 +197,7 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
}
return match it.node {
ast::ItemStatic(..) | ast::ItemFn(..) |
ast::ItemConst(..) | ast::ItemStatic(..) | ast::ItemFn(..) |
ast::ItemForeignMod(..) | ast::ItemTy(..) => {
None
}

View File

@ -384,6 +384,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TermsContext<'a, 'tcx> {
ast::ItemImpl(..) |
ast::ItemStatic(..) |
ast::ItemConst(..) |
ast::ItemFn(..) |
ast::ItemMod(..) |
ast::ItemForeignMod(..) |
@ -528,6 +529,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> {
}
ast::ItemStatic(..) |
ast::ItemConst(..) |
ast::ItemFn(..) |
ast::ItemMod(..) |
ast::ItemForeignMod(..) |

View File

@ -1309,6 +1309,7 @@ pub struct Item {
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
pub enum Item_ {
ItemStatic(P<Ty>, Mutability, P<Expr>),
ItemConst(P<Ty>, P<Expr>),
ItemFn(P<FnDecl>, FnStyle, Abi, Generics, P<Block>),
ItemMod(Mod),
ItemForeignMod(ForeignMod),

View File

@ -1018,6 +1018,7 @@ fn node_id_to_string(map: &Map, id: NodeId) -> String {
let path_str = map.path_to_str_with_ident(id, item.ident);
let item_str = match item.node {
ItemStatic(..) => "static",
ItemConst(..) => "const",
ItemFn(..) => "fn",
ItemMod(..) => "mod",
ItemForeignMod(..) => "foreign mod",

View File

@ -12,8 +12,6 @@ use abi::Abi;
use ast::*;
use ast;
use ast_util;
use attr::{InlineNever, InlineNone};
use attr;
use codemap;
use codemap::Span;
use owned_slice::OwnedSlice;
@ -706,18 +704,6 @@ pub fn lit_is_str(lit: &Lit) -> bool {
}
}
/// Returns true if the static with the given mutability and attributes
/// has a significant address and false otherwise.
pub fn static_has_significant_address(mutbl: ast::Mutability,
attrs: &[ast::Attribute])
-> bool {
if mutbl == ast::MutMutable {
return true
}
let inline = attr::find_inline_attr(attrs);
inline == InlineNever || inline == InlineNone
}
/// Macro invocations are guaranteed not to occur after expansion is complete.
/// Extracting fields of a method requires a dynamic check to make sure that it's
/// not a macro invocation. This check is guaranteed to succeed, assuming

View File

@ -250,6 +250,13 @@ pub trait AstBuilder {
expr: P<ast::Expr>)
-> P<ast::Item>;
fn item_const(&self,
span: Span,
name: Ident,
ty: P<ast::Ty>,
expr: P<ast::Expr>)
-> P<ast::Item>;
fn item_ty_poly(&self,
span: Span,
name: Ident,
@ -1033,6 +1040,15 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
self.item(span, name, Vec::new(), ast::ItemStatic(ty, mutbl, expr))
}
fn item_const(&self,
span: Span,
name: Ident,
ty: P<ast::Ty>,
expr: P<ast::Expr>)
-> P<ast::Item> {
self.item(span, name, Vec::new(), ast::ItemConst(ty, expr))
}
fn item_ty_poly(&self, span: Span, name: Ident, ty: P<ast::Ty>,
generics: Generics) -> P<ast::Item> {
self.item(span, name, Vec::new(), ast::ItemTy(ty, generics))

View File

@ -903,6 +903,9 @@ pub fn noop_fold_item_underscore<T: Folder>(i: Item_, folder: &mut T) -> Item_ {
ItemStatic(t, m, e) => {
ItemStatic(folder.fold_ty(t), m, folder.fold_expr(e))
}
ItemConst(t, e) => {
ItemConst(folder.fold_ty(t), folder.fold_expr(e))
}
ItemFn(decl, fn_style, abi, generics, body) => {
ItemFn(
folder.fold_fn_decl(decl),

View File

@ -32,7 +32,7 @@ use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind};
use ast::{FnOnceUnboxedClosureKind};
use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod};
use ast::{Ident, NormalFn, Inherited, ImplItem, Item, Item_, ItemStatic};
use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl};
use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemConst};
use ast::{ItemMac, ItemMod, ItemStruct, ItemTrait, ItemTy};
use ast::{LifetimeDef, Lit, Lit_};
use ast::{LitBool, LitChar, LitByte, LitBinary};
@ -4739,14 +4739,18 @@ impl<'a> Parser<'a> {
}
}
fn parse_item_const(&mut self, m: Mutability) -> ItemInfo {
fn parse_item_const(&mut self, m: Option<Mutability>) -> ItemInfo {
let id = self.parse_ident();
self.expect(&token::COLON);
let ty = self.parse_ty(true);
self.expect(&token::EQ);
let e = self.parse_expr();
self.commit_expr_expecting(&*e, token::SEMI);
(id, ItemStatic(ty, m, e), None)
let item = match m {
Some(m) => ItemStatic(ty, m, e),
None => ItemConst(ty, e),
};
(id, item, None)
}
/// Parse a `mod <foo> { ... }` or `mod <foo>;` item
@ -5296,7 +5300,7 @@ impl<'a> Parser<'a> {
// STATIC ITEM
self.bump();
let m = if self.eat_keyword(keywords::Mut) {MutMutable} else {MutImmutable};
let (ident, item_, extra_attrs) = self.parse_item_const(m);
let (ident, item_, extra_attrs) = self.parse_item_const(Some(m));
let last_span = self.last_span;
let item = self.mk_item(lo,
last_span.hi,
@ -5314,7 +5318,7 @@ impl<'a> Parser<'a> {
self.span_err(last_span, "const globals cannot be mutable, \
did you mean to declare a static?");
}
let (ident, item_, extra_attrs) = self.parse_item_const(MutImmutable);
let (ident, item_, extra_attrs) = self.parse_item_const(None);
let last_span = self.last_span;
let item = self.mk_item(lo,
last_span.hi,

View File

@ -757,6 +757,20 @@ impl<'a> State<'a> {
try!(word(&mut self.s, ";"));
try!(self.end()); // end the outer cbox
}
ast::ItemConst(ref ty, ref expr) => {
try!(self.head(visibility_qualified(item.vis,
"const").as_slice()));
try!(self.print_ident(item.ident));
try!(self.word_space(":"));
try!(self.print_type(&**ty));
try!(space(&mut self.s));
try!(self.end()); // end the head-ibox
try!(self.word_space("="));
try!(self.print_expr(&**expr));
try!(word(&mut self.s, ";"));
try!(self.end()); // end the outer cbox
}
ast::ItemFn(ref decl, fn_style, abi, ref typarams, ref body) => {
try!(self.print_fn(
&**decl,

View File

@ -493,11 +493,10 @@ fn mk_tests(cx: &TestCtxt) -> P<ast::Item> {
Some(static_lt),
ast::MutImmutable);
// static TESTS: $static_type = &[...];
ecx.item_static(sp,
ecx.ident_of("TESTS"),
static_type,
ast::MutImmutable,
test_descs)
ecx.item_const(sp,
ecx.ident_of("TESTS"),
static_type,
test_descs)
}
fn is_test_crate(krate: &ast::Crate) -> bool {

View File

@ -211,7 +211,8 @@ pub fn walk_trait_ref_helper<'v,V>(visitor: &mut V, trait_ref: &'v TraitRef)
pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
visitor.visit_ident(item.span, item.ident);
match item.node {
ItemStatic(ref typ, _, ref expr) => {
ItemStatic(ref typ, _, ref expr) |
ItemConst(ref typ, ref expr) => {
visitor.visit_ty(&**typ);
visitor.visit_expr(&**expr);
}

View File

@ -0,0 +1,19 @@
// Copyright 2014 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.
struct A;
impl Drop for A {
fn drop(&mut self) {}
}
const FOO: A = A;
//~ ERROR: constants are not allowed to have destructors
fn main() {}