From 5f0a123f0d1c8718b015c973b3780bc9353e9159 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Sun, 3 Mar 2013 14:53:15 -0800 Subject: [PATCH 1/3] Construct const fns based on the type, not the definition. Otherwise we can add a null environment when we shouldn't. Fixes #5210. --- src/librustc/middle/trans/consts.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 36cda3dfbe9..3cfafde1043 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -394,13 +394,22 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { ast::expr_path(pth) => { assert pth.types.len() == 0; match cx.tcx.def_map.find(&e.id) { - Some(ast::def_fn(def_id, purity)) => { + Some(ast::def_fn(def_id, _purity)) => { assert ast_util::is_local(def_id); let f = base::get_item_val(cx, def_id.node); - match purity { - ast::extern_fn => - llvm::LLVMConstPointerCast(f, T_ptr(T_i8())), - _ => C_struct(~[f, C_null(T_opaque_box_ptr(cx))]) + let ety = ty::expr_ty_adjusted(cx.tcx, e); + match ty::get(ety).sty { + ty::ty_bare_fn(*) | ty::ty_ptr(*) => { + llvm::LLVMConstPointerCast(f, T_ptr(T_i8())) + } + ty::ty_closure(*) => { + C_struct(~[f, C_null(T_opaque_box_ptr(cx))]) + } + _ => { + cx.sess.span_bug(e.span, fmt!( + "unexpected const fn type: %s", + ty_to_str(cx.tcx, ety))) + } } } Some(ast::def_const(def_id)) => { From 514fd3efecccdaf311ee4549c75d948348f6d319 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Sun, 3 Mar 2013 14:30:06 -0800 Subject: [PATCH 2/3] Assert that constants are translated with the correct size. --- src/librustc/middle/trans/consts.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 3cfafde1043..e61afbdce25 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -20,6 +20,7 @@ use middle::trans::expr; use middle::trans::machine; use middle::trans::type_of; use middle::ty; +use util::ppaux::{expr_repr, ty_to_str}; use core::libc::c_uint; use syntax::{ast, ast_util, codemap, ast_map}; @@ -150,6 +151,24 @@ pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef { } pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef { + let ety = ty::expr_ty_adjusted(cx.tcx, e); + let llty = type_of::sizing_type_of(cx, ety); + let llconst = const_expr_unchecked(cx, e); + let csize = machine::llsize_of_alloc(cx, val_ty(llconst)); + let tsize = machine::llsize_of_alloc(cx, llty); + if csize != tsize { + unsafe { + llvm::LLVMDumpValue(llconst); + llvm::LLVMDumpValue(C_null(llty)); + } + cx.sess.bug(fmt!("const %s of type %s has size %u instead of %u", + expr_repr(cx.tcx, e), ty_to_str(cx.tcx, ety), + csize, tsize)); + } + llconst +} + +fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { unsafe { let _icx = cx.insn_ctxt("const_expr"); return match /*bad*/copy e.node { From eb1c632201a9b9f03abfef816fb9625d3f014600 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Sun, 3 Mar 2013 17:18:18 -0800 Subject: [PATCH 3/3] Add a test case for const fn sizing, just to be safe. --- src/test/run-pass/const-vec-of-fns.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/test/run-pass/const-vec-of-fns.rs diff --git a/src/test/run-pass/const-vec-of-fns.rs b/src/test/run-pass/const-vec-of-fns.rs new file mode 100644 index 00000000000..bf7472aeb36 --- /dev/null +++ b/src/test/run-pass/const-vec-of-fns.rs @@ -0,0 +1,27 @@ +// Copyright 2013 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. + +/*! + * Try to double-check that const fns have the right size (with or + * without dummy env ptr, as appropriate) by iterating a size-2 array. + * If the const size differs from the runtime size, the second element + * should be read as a null or otherwise wrong pointer and crash. + */ + +fn f() { } +const bare_fns: &[extern fn()] = &[f, f]; +// NOTE Why does this not type without the struct? +struct S(&fn()); +const closures: &[S] = &[S(f), S(f)]; + +pub fn main() { + for bare_fns.each |&bare_fn| { bare_fn() } + for closures.each |&closure| { (*closure)() } +}