Skip to content

Allow paths in constants to refer to polymorphic items. #20295

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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::{self, 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