Skip to content

Commit

Permalink
Merge pull request #20295 from eddyb/poly-const
Browse files Browse the repository at this point in the history
Allow paths in constants to refer to polymorphic items.

Reviewed-by: nikomatsakis
  • Loading branch information
bors committed Jan 4, 2015
2 parents c6c7866 + c37e7ae commit 6c12a3d
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 272 deletions.
135 changes: 40 additions & 95 deletions src/librustc/middle/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ use middle::ty;
use util::ppaux;

use syntax::ast;
use syntax::ast_util;
use syntax::visit::Visitor;
use syntax::visit;
use syntax::visit::{mod, Visitor};

struct CheckCrateVisitor<'a, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>,
Expand All @@ -37,24 +35,39 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
{
self.with_const(true, f);
}
fn outside_const<F>(&mut self, f: F) where
F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>),
{
self.with_const(false, f);
}
}

impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
fn visit_item(&mut self, i: &ast::Item) {
check_item(self, i);
match i.node {
ast::ItemStatic(_, _, ref ex) |
ast::ItemConst(_, ref ex) => {
self.inside_const(|v| v.visit_expr(&**ex));
}
ast::ItemEnum(ref enum_definition, _) => {
self.inside_const(|v| {
for var in enum_definition.variants.iter() {
if let Some(ref ex) = var.node.disr_expr {
v.visit_expr(&**ex);
}
}
});
}
_ => self.with_const(false, |v| visit::walk_item(v, i))
}
}
fn visit_pat(&mut self, p: &ast::Pat) {
check_pat(self, p);
let is_const = match p.node {
ast::PatLit(_) | ast::PatRange(..) => true,
_ => false
};
self.with_const(is_const, |v| visit::walk_pat(v, p))
}
fn visit_expr(&mut self, ex: &ast::Expr) {
if check_expr(self, ex) {
visit::walk_expr(self, ex);
if self.in_const {
check_expr(self, ex);
}
visit::walk_expr(self, ex);
}
}

Expand All @@ -64,57 +77,13 @@ pub fn check_crate(tcx: &ty::ctxt) {
tcx.sess.abort_if_errors();
}

fn check_item(v: &mut CheckCrateVisitor, it: &ast::Item) {
match it.node {
ast::ItemStatic(_, _, ref ex) |
ast::ItemConst(_, ref ex) => {
v.inside_const(|v| v.visit_expr(&**ex));
}
ast::ItemEnum(ref enum_definition, _) => {
for var in (*enum_definition).variants.iter() {
for ex in var.node.disr_expr.iter() {
v.inside_const(|v| v.visit_expr(&**ex));
}
}
}
_ => v.outside_const(|v| visit::walk_item(v, it))
}
}

fn check_pat(v: &mut CheckCrateVisitor, p: &ast::Pat) {
fn is_str(e: &ast::Expr) -> bool {
match e.node {
ast::ExprBox(_, ref expr) => {
match expr.node {
ast::ExprLit(ref lit) => ast_util::lit_is_str(&**lit),
_ => false,
}
}
_ => false,
}
}
match p.node {
// Let through plain ~-string literals here
ast::PatLit(ref a) => if !is_str(&**a) { v.inside_const(|v| v.visit_expr(&**a)); },
ast::PatRange(ref a, ref b) => {
if !is_str(&**a) { v.inside_const(|v| v.visit_expr(&**a)); }
if !is_str(&**b) { v.inside_const(|v| v.visit_expr(&**b)); }
}
_ => v.outside_const(|v| visit::walk_pat(v, p))
}
}

fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) -> bool {
if !v.in_const { return true }

fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) {
match e.node {
ast::ExprUnary(ast::UnDeref, _) => {}
ast::ExprUnary(ast::UnUniq, _) => {
span_err!(v.tcx.sess, e.span, E0010,
"cannot do allocations in constant expressions");
return false;
}
ast::ExprLit(ref lit) if ast_util::lit_is_str(&**lit) => {}
ast::ExprBinary(..) | ast::ExprUnary(..) => {
let method_call = ty::MethodCall::expr(e.id);
if v.tcx.method_map.borrow().contains_key(&method_call) {
Expand All @@ -123,7 +92,7 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) -> bool {
expressions");
}
}
ast::ExprLit(_) => (),
ast::ExprLit(_) => {}
ast::ExprCast(ref from, _) => {
let toty = ty::expr_ty(v.tcx, e);
let fromty = ty::expr_ty(v.tcx, &**from);
Expand All @@ -142,39 +111,23 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) -> bool {
expression");
}
}
ast::ExprPath(ref pth) => {
// NB: In the future you might wish to relax this slightly
// to handle on-demand instantiation of functions via
// foo::<bar> in a const. Currently that is only done on
// a path in trans::callee that only works in block contexts.
if !pth.segments.iter().all(|segment| segment.parameters.is_empty()) {
span_err!(v.tcx.sess, e.span, E0013,
"paths in constants may only refer to items without \
type parameters");
}
match v.tcx.def_map.borrow().get(&e.id) {
Some(&DefStatic(..)) |
Some(&DefConst(..)) |
Some(&DefFn(..)) |
Some(&DefVariant(_, _, _)) |
Some(&DefStruct(_)) => { }
ast::ExprPath(_) => {
match v.tcx.def_map.borrow()[e.id] {
DefStatic(..) | DefConst(..) |
DefFn(..) | DefStaticMethod(..) | DefMethod(..) |
DefStruct(_) | DefVariant(_, _, _) => {}

Some(&def) => {
def => {
debug!("(checking const) found bad def: {}", def);
span_err!(v.tcx.sess, e.span, E0014,
"paths in constants may only refer to constants \
or functions");
}
None => {
v.tcx.sess.span_bug(e.span, "unbound path in const?!");
}
}
}
ast::ExprCall(ref callee, _) => {
match v.tcx.def_map.borrow().get(&callee.id) {
Some(&DefStruct(..)) |
Some(&DefVariant(..)) => {} // OK.

match v.tcx.def_map.borrow()[callee.id] {
DefStruct(..) | DefVariant(..) => {} // OK.
_ => {
span_err!(v.tcx.sess, e.span, E0015,
"function calls in constants are limited to \
Expand All @@ -190,9 +143,9 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) -> bool {
"blocks in constants are limited to items and \
tail expressions");
match stmt.node {
ast::StmtDecl(ref span, _) => {
match span.node {
ast::DeclLocal(_) => block_span_err(span.span),
ast::StmtDecl(ref decl, _) => {
match decl.node {
ast::DeclLocal(_) => block_span_err(decl.span),

// Item statements are allowed
ast::DeclItem(_) => {}
Expand All @@ -206,10 +159,6 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) -> bool {
}
}
}
match block.expr {
Some(ref expr) => { check_expr(v, &**expr); }
None => {}
}
}
ast::ExprVec(_) |
ast::ExprAddrOf(ast::MutImmutable, _) |
Expand All @@ -232,11 +181,7 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &ast::Expr) -> bool {
}
}

_ => {
span_err!(v.tcx.sess, e.span, E0019,
"constant contains unimplemented expression type");
return false;
}
_ => span_err!(v.tcx.sess, e.span, E0019,
"constant contains unimplemented expression type")
}
true
}
6 changes: 3 additions & 3 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,14 +559,14 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
id, expr_ty.repr(self.tcx()), def);

match def {
def::DefStruct(..) | def::DefVariant(..) | def::DefFn(..) |
def::DefStaticMethod(..) | def::DefConst(..) => {
def::DefStruct(..) | def::DefVariant(..) | def::DefConst(..) |
def::DefFn(..) | def::DefStaticMethod(..) | def::DefMethod(..) => {
Ok(self.cat_rvalue_node(id, span, expr_ty))
}
def::DefMod(_) | def::DefForeignMod(_) | def::DefUse(_) |
def::DefTrait(_) | def::DefTy(..) | def::DefPrimTy(_) |
def::DefTyParam(..) | def::DefTyParamBinder(..) | def::DefRegion(_) |
def::DefLabel(_) | def::DefSelfTy(..) | def::DefMethod(..) |
def::DefLabel(_) | def::DefSelfTy(..) |
def::DefAssociatedTy(..) | def::DefAssociatedPath(..)=> {
Ok(Rc::new(cmt_ {
id:id,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ pub fn get_res_dtor<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// Since we're in trans we don't care for any region parameters
let substs = subst::Substs::erased(substs.types.clone());

let (val, _) = monomorphize::monomorphic_fn(ccx, did, &substs, None);
let (val, _, _) = monomorphize::monomorphic_fn(ccx, did, &substs, None);

val
} else if did.krate == ast::LOCAL_CRATE {
Expand Down
Loading

0 comments on commit 6c12a3d

Please sign in to comment.