Skip to content

Commit

Permalink
rustc: Add const globals to the language
Browse files Browse the repository at this point in the history
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]: rust-lang/rfcs#246
  • Loading branch information
alexcrichton committed Oct 9, 2014
1 parent a89ad58 commit 90d03d7
Show file tree
Hide file tree
Showing 46 changed files with 721 additions and 445 deletions.
5 changes: 3 additions & 2 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)),
Expand Down
24 changes: 14 additions & 10 deletions src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ pub struct EncodeParams<'a, 'tcx: 'a> {
pub tcx: &'a ty::ctxt<'tcx>,
pub reexports2: &'a middle::resolve::ExportMap2,
pub item_symbols: &'a RefCell<NodeMap<String>>,
pub non_inlineable_statics: &'a RefCell<NodeSet>,
pub link_meta: &'a LinkMeta,
pub cstore: &'a cstore::CStore,
pub encode_inlined_item: EncodeInlinedItem<'a>,
Expand All @@ -81,7 +80,6 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
pub tcx: &'a ty::ctxt<'tcx>,
pub reexports2: &'a middle::resolve::ExportMap2,
pub item_symbols: &'a RefCell<NodeMap<String>>,
pub non_inlineable_statics: &'a RefCell<NodeSet>,
pub link_meta: &'a LinkMeta,
pub cstore: &'a cstore::CStore,
pub encode_inlined_item: RefCell<EncodeInlinedItem<'a>>,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand All @@ -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),
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/astencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 5 additions & 9 deletions src/librustc/middle/borrowck/gather_loans/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,11 @@ fn check_aliasability(bccx: &BorrowckCtxt,
// Borrow of an immutable static item:
match safety {
mc::InteriorUnsafe => {
// If the static item contains an Unsafe<T>, it has interior mutability.
// In such cases, we cannot permit it to be borrowed, because the
// static item resides in immutable memory and mutating it would
// cause segfaults.
bccx.tcx.sess.span_err(borrow_span,
"borrow of immutable static items \
with unsafe interior is not \
allowed");
Err(())
// If the static item contains an Unsafe<T>, it has interior
// mutability. In such cases, another phase of the compiler
// will ensure that the type is `Sync` and then trans will
// not put it in rodata, so this is ok to allow.
Ok(())
}
mc::InteriorSafe => {
// Immutable static can be borrowed, no problem.
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/middle/borrowck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
_ => {
Expand Down
8 changes: 5 additions & 3 deletions src/librustc/middle/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand All @@ -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, _) => {
Expand Down Expand Up @@ -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(_)) => { }
Expand Down Expand Up @@ -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 => {}
}
}
Expand Down
64 changes: 37 additions & 27 deletions src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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
}
},
Expand Down Expand Up @@ -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)]
};
Expand All @@ -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());
Expand All @@ -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
}
Expand All @@ -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)
},
Expand Down Expand Up @@ -722,40 +729,42 @@ fn range_covered_by_constructor(ctor: &Constructor,
/// different patterns.
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
/// fields filled with wild patterns.
pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat], dummy: &'a Pat,
constructor: &Constructor, col: uint, arity: uint) -> Option<Vec<&'a Pat>> {
let &Pat {
id: pat_id, node: ref node, span: pat_span
} = raw_pat(r[col]);
let head: Option<Vec<&Pat>> = match node {

&PatWild(_) =>
Some(Vec::from_elem(arity, &DUMMY_WILD_PAT)),
Some(Vec::from_elem(arity, dummy)),

&PatIdent(_, _, _) => {
let opt_def = cx.tcx.def_map.borrow().find_copy(&pat_id);
match opt_def {
Some(DefStatic(..)) =>
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
Some(DefConst(..)) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
Some(DefVariant(_, id, _)) => if *constructor == Variant(id) {
Some(vec!())
} else {
None
},
_ => Some(Vec::from_elem(arity, &DUMMY_WILD_PAT))
_ => Some(Vec::from_elem(arity, dummy))
}
}

&PatEnum(_, ref args) => {
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
match def {
DefStatic(..) =>
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
DefConst(..) =>
cx.tcx.sess.span_bug(pat_span, "const pattern should've \
been rewritten"),
DefVariant(_, id, _) if *constructor != Variant(id) => None,
DefVariant(..) | DefStruct(..) => {
Some(match args {
&Some(ref args) => args.iter().map(|p| &**p).collect(),
&None => Vec::from_elem(arity, &DUMMY_WILD_PAT)
&None => Vec::from_elem(arity, dummy)
})
}
_ => None
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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)
},
Expand Down
Loading

0 comments on commit 90d03d7

Please sign in to comment.