From 90d03d792669fed99b659d1efbe835d4b9b8873c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 6 Oct 2014 08:17:01 -0700 Subject: [PATCH] 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 --- src/librustc/lint/builtin.rs | 5 +- src/librustc/metadata/decoder.rs | 5 +- src/librustc/metadata/encoder.rs | 24 +- src/librustc/middle/astencode.rs | 1 + .../middle/borrowck/gather_loans/mod.rs | 14 +- src/librustc/middle/borrowck/mod.rs | 3 +- src/librustc/middle/check_const.rs | 8 +- src/librustc/middle/check_match.rs | 64 ++-- src/librustc/middle/check_static.rs | 307 ++++++++++++++---- src/librustc/middle/check_static_recursion.rs | 28 +- src/librustc/middle/const_eval.rs | 6 +- src/librustc/middle/dead.rs | 3 +- src/librustc/middle/def.rs | 3 +- src/librustc/middle/expr_use_visitor.rs | 2 +- src/librustc/middle/mem_categorization.rs | 4 +- src/librustc/middle/pat_util.rs | 2 +- src/librustc/middle/privacy.rs | 5 +- src/librustc/middle/reachable.rs | 39 +-- src/librustc/middle/resolve.rs | 21 +- src/librustc/middle/resolve_lifetime.rs | 2 +- src/librustc/middle/save/mod.rs | 29 ++ src/librustc/middle/traexpr | 0 src/librustc/middle/trans/_match.rs | 48 ++- src/librustc/middle/trans/base.rs | 110 +++---- src/librustc/middle/trans/callee.rs | 1 + src/librustc/middle/trans/consts.rs | 206 ++++++------ src/librustc/middle/trans/context.rs | 21 +- src/librustc/middle/trans/debuginfo.rs | 1 + src/librustc/middle/trans/expr.rs | 75 +++-- src/librustc/middle/trans/inline.rs | 17 +- src/librustc/middle/ty.rs | 3 + src/librustc/middle/typeck/check/mod.rs | 6 +- src/librustc/middle/typeck/check/wf.rs | 3 + src/librustc/middle/typeck/collect.rs | 2 +- src/librustc/middle/typeck/infer/test.rs | 2 +- src/librustc/middle/typeck/variance.rs | 2 + src/libsyntax/ast.rs | 1 + src/libsyntax/ast_map/mod.rs | 1 + src/libsyntax/ast_util.rs | 14 - src/libsyntax/ext/build.rs | 16 + src/libsyntax/fold.rs | 3 + src/libsyntax/parse/parser.rs | 14 +- src/libsyntax/print/pprust.rs | 14 + src/libsyntax/test.rs | 9 +- src/libsyntax/visit.rs | 3 +- .../issue-17718-const-destructors.rs | 19 ++ 46 files changed, 721 insertions(+), 445 deletions(-) create mode 100644 src/librustc/middle/traexpr create mode 100644 src/test/compile-fail/issue-17718-const-destructors.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 4c147517a7f03..451d39fbc3def 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -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, diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 58d0f132e06a2..dcf394aa3f406 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -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)), diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 642f66e259a62..94d86956f7005 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -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>, - pub non_inlineable_statics: &'a RefCell, 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>, - pub non_inlineable_statics: &'a RefCell, pub link_meta: &'a LinkMeta, pub cstore: &'a cstore::CStore, pub encode_inlined_item: RefCell>, @@ -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), diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 2bd145706aaad..955228c99deed 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -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) diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index cd003432ef22c..f2ff104ba1d10 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -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, 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, 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. diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index a86ae42006595..7d0d99443b0d8 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -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); } _ => { diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index bb62a96f0adc6..f0455db6e3bb6 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -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 => {} } } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 92f04b108c19b..297640707687c 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -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> { 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> = 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) }, diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs index 64e4d7ff28425..9cc1f92dc9304 100644 --- a/src/librustc/middle/check_static.rs +++ b/src/librustc/middle/check_static.rs @@ -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::print::pprust; use syntax::visit::Visitor; +use syntax::codemap::{DUMMY_SP, Span}; use syntax::visit; -use syntax::print::pprust; - - -fn safe_type_for_static_mut(cx: &ty::ctxt, e: &ast::Expr) -> Option { - 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) {} +} + diff --git a/src/librustc/middle/check_static_recursion.rs b/src/librustc/middle/check_static_recursion.rs index b571a18c1ece7..1f76d9dba2635 100644 --- a/src/librustc/middle/check_static_recursion.rs +++ b/src/librustc/middle/check_static_recursion.rs @@ -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 + idstack: Vec } // 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)); } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 98d2cefac0fcf..abcdc45bdcfde 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -87,7 +87,7 @@ pub fn join_all>(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 diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 1b2f62a9385a2..ff3720381009c 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -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); } _ => () diff --git a/src/librustc/middle/def.rs b/src/librustc/middle/def.rs index bd42586435f26..3b7af9788ac57 100644 --- a/src/librustc/middle/def.rs +++ b/src/librustc/middle/def.rs @@ -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) | diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 51cdbfcf2514c..6cfdac93efc8a 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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)); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 9a0885ca30135..fa494b357c19d 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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))); } diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs index e3806f02ed4c8..4d61baca70874 100644 --- a/src/librustc/middle/pat_util.rs +++ b/src/librustc/middle/pat_util.rs @@ -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 } } diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 7e415d1f5c1a7..e434d859993c0 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -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(..) => {} } diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 555a033a568d1..5d6f7048b82e1 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -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(..) => {} diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 595739585a398..318bc05d05f88 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -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; } _ => { diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 42ae877122490..6d84b8cb49dd2 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -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; } diff --git a/src/librustc/middle/save/mod.rs b/src/librustc/middle/save/mod.rs index debcfe3933c03..4aba29d7bae16 100644 --- a/src/librustc/middle/save/mod.rs +++ b/src/librustc/middle/save/mod.rs @@ -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) } diff --git a/src/librustc/middle/traexpr b/src/librustc/middle/traexpr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 38f09d1957252..9eb02717f04a1 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -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>; +type EnterPatterns<'a, 'p> = |&[&'p ast::Pat]|: 'a -> Option>; -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> { +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> { 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>> = 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(), })); diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index d175919fb8108..8df5b375a8167 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -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, diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 7942a0211e4e6..bc562b39c98f7 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -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) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 5e4692bd0f617..ec357f7bfd7c6 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -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((a, b, _): (R, S, T)) -> (R, S) { - (a, b) -} - fn const_vec(cx: &CrateContext, e: &ast::Expr, - es: &[P], is_local: bool) -> (ValueRef, Type, bool) { + es: &[P]) -> (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::>(); // 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]| { - 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::>(); + 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) +} diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 5c8c6a24b4fa5..ee5ba61a295cf 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -66,10 +66,6 @@ pub struct SharedCrateContext<'tcx> { reachable: NodeSet, item_symbols: RefCell>, 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, symbol_hasher: RefCell, tcx: ty::ctxt<'tcx>, stats: Stats, @@ -121,6 +117,9 @@ pub struct LocalCrateContext { /// Cache of emitted const values const_values: RefCell>, + /// Cache of emitted static values + static_values: RefCell>, + /// Cache of external const values extern_const_values: RefCell>, @@ -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 { - &self.non_inlineable_statics - } - pub fn symbol_hasher<'a>(&'a self) -> &'a RefCell { &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 { - &self.shared.non_inlineable_statics - } - pub fn monomorphized<'a>(&'a self) -> &'a RefCell> { &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> { + &self.local.static_values + } + pub fn extern_const_values<'a>(&'a self) -> &'a RefCell> { &self.local.extern_const_values } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index a2b7cb159d9dc..ec92f935c49be 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -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, diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index cd4ad85d0942c..19c9c5e0119d2 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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()) } diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index e5c8d4d0ab343..048402782a6d9 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -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 { @@ -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!(), }; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 10bc1da3acb54..e1491c1f49be2 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 4af71bfda8b35..68bb3fcf94544 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -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)); diff --git a/src/librustc/middle/typeck/check/wf.rs b/src/librustc/middle/typeck/check/wf.rs index 587aa072fa863..4c3cec1aff42f 100644 --- a/src/librustc/middle/typeck/check/wf.rs +++ b/src/librustc/middle/typeck/check/wf.rs @@ -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)] diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 40c52fd36b9c1..0b5c86fea7123 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -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); diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs index 29cd2e77e8b1f..6ef2143b624f8 100644 --- a/src/librustc/middle/typeck/infer/test.rs +++ b/src/librustc/middle/typeck/infer/test.rs @@ -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 } diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs index 9317ba2c7fa93..60a7aa7790442 100644 --- a/src/librustc/middle/typeck/variance.rs +++ b/src/librustc/middle/typeck/variance.rs @@ -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(..) | diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0a87c0a344e52..c47d6b0fc9dc8 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1309,6 +1309,7 @@ pub struct Item { #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub enum Item_ { ItemStatic(P, Mutability, P), + ItemConst(P, P), ItemFn(P, FnStyle, Abi, Generics, P), ItemMod(Mod), ItemForeignMod(ForeignMod), diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs index 257bfc632d882..2d0cea2fefc9d 100644 --- a/src/libsyntax/ast_map/mod.rs +++ b/src/libsyntax/ast_map/mod.rs @@ -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", diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index f746e1f14822a..f51c2985f0bf4 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -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 diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 1fdb6dd505f45..87983e1aea3a9 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -250,6 +250,13 @@ pub trait AstBuilder { expr: P) -> P; + fn item_const(&self, + span: Span, + name: Ident, + ty: P, + expr: P) + -> P; + 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, + expr: P) + -> P { + self.item(span, name, Vec::new(), ast::ItemConst(ty, expr)) + } + fn item_ty_poly(&self, span: Span, name: Ident, ty: P, generics: Generics) -> P { self.item(span, name, Vec::new(), ast::ItemTy(ty, generics)) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 84de6c3b91339..32e226361e9d4 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -903,6 +903,9 @@ pub fn noop_fold_item_underscore(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), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9bfb593786ef1..e73ffd7e58123 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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) -> 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 { ... }` or `mod ;` 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, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index c3a3848019a5d..321b00db47eaf 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -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, diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 3f5b524a9b5b2..9fb5742bb9b15 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -493,11 +493,10 @@ fn mk_tests(cx: &TestCtxt) -> P { 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 { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 249f87d3102dd..5c7b144f4ab6e 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -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); } diff --git a/src/test/compile-fail/issue-17718-const-destructors.rs b/src/test/compile-fail/issue-17718-const-destructors.rs new file mode 100644 index 0000000000000..dffbfe155646a --- /dev/null +++ b/src/test/compile-fail/issue-17718-const-destructors.rs @@ -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 or the MIT license +// , 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() {}