From 9b1865a7faafe16af9eb9f804e82065b5a6f1e24 Mon Sep 17 00:00:00 2001 From: Jason Fager Date: Wed, 29 Jan 2014 00:05:11 -0500 Subject: [PATCH] Add a limited prim type lookup for safer const expr evaluation --- src/librustc/middle/const_eval.rs | 15 +- src/librustc/middle/ty.rs | 19 +- src/librustc/middle/typeck/astconv.rs | 425 +++++++++++++----------- src/librustc/middle/typeck/check/mod.rs | 1 - src/test/run-pass/issue-5900.rs | 21 ++ src/test/run-pass/issue-9942.rs | 13 + 6 files changed, 285 insertions(+), 209 deletions(-) create mode 100644 src/test/run-pass/issue-5900.rs create mode 100644 src/test/run-pass/issue-9942.rs diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 770275f3f6a..59057128555 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -11,7 +11,9 @@ use metadata::csearch; use middle::astencode; + use middle::ty; +use middle::typeck::astconv; use middle; use syntax::{ast, ast_map, ast_util}; @@ -445,8 +447,17 @@ pub fn eval_const_expr_partial(tcx: &T, e: &Expr) _ => Err(~"Bad operands for binary") } } - ExprCast(base, _) => { - let ety = tcx.expr_ty(e); + ExprCast(base, target_ty) => { + // This tends to get called w/o the type actually having been + // populated in the ctxt, which was causing things to blow up + // (#5900). Fall back to doing a limited lookup to get past it. + let ety = ty::expr_ty_opt(tcx.ty_ctxt(), e) + .or_else(|| astconv::ast_ty_to_prim_ty(tcx.ty_ctxt(), target_ty)) + .unwrap_or_else(|| tcx.ty_ctxt().sess.span_fatal( + target_ty.span, + format!("Target type not found for const cast") + )); + let base = eval_const_expr_partial(tcx, base); match base { Err(_) => base, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index a31b8413550..be03edcdba4 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2617,10 +2617,8 @@ pub fn node_id_to_trait_ref(cx: ctxt, id: ast::NodeId) -> @ty::TraitRef { } pub fn node_id_to_type(cx: ctxt, id: ast::NodeId) -> t { - //printfln!("{:?}/{:?}", id, cx.node_types.len()); - let node_types = cx.node_types.borrow(); - match node_types.get().find(&(id as uint)) { - Some(&t) => t, + match node_id_to_type_opt(cx, id) { + Some(t) => t, None => cx.sess.bug( format!("node_id_to_type: no type for node `{}`", ast_map::node_id_to_str(cx.items, id, @@ -2628,6 +2626,15 @@ pub fn node_id_to_type(cx: ctxt, id: ast::NodeId) -> t { } } +pub fn node_id_to_type_opt(cx: ctxt, id: ast::NodeId) -> Option { + let node_types = cx.node_types.borrow(); + debug!("id: {:?}, node_types: {:?}", id, node_types); + match node_types.get().find(&(id as uint)) { + Some(&t) => Some(t), + None => None + } +} + // FIXME(pcwalton): Makes a copy, bleh. Probably better to not do that. pub fn node_id_to_type_params(cx: ctxt, id: ast::NodeId) -> ~[t] { let node_type_substs = cx.node_type_substs.borrow(); @@ -2798,6 +2805,10 @@ pub fn expr_ty(cx: ctxt, expr: &ast::Expr) -> t { return node_id_to_type(cx, expr.id); } +pub fn expr_ty_opt(cx: ctxt, expr: &ast::Expr) -> Option { + return node_id_to_type_opt(cx, expr.id); +} + pub fn expr_ty_adjusted(cx: ctxt, expr: &ast::Expr) -> t { /*! * diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index f1e1f379b0c..466c9192b8a 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -276,6 +276,74 @@ pub fn ast_path_to_ty( pub static NO_REGIONS: uint = 1; pub static NO_TPS: uint = 2; +fn check_path_args(tcx: ty::ctxt, + path: &ast::Path, + flags: uint) { + if (flags & NO_TPS) != 0u { + if !path.segments.iter().all(|s| s.types.is_empty()) { + tcx.sess.span_err( + path.span, + "type parameters are not allowed on this type"); + } + } + + if (flags & NO_REGIONS) != 0u { + if !path.segments.last().unwrap().lifetimes.is_empty() { + tcx.sess.span_err( + path.span, + "region parameters are not allowed on this type"); + } + } +} + +pub fn ast_ty_to_prim_ty(tcx: ty::ctxt, ast_ty: &ast::Ty) -> Option { + match ast_ty.node { + ast::TyPath(ref path, _, id) => { + let def_map = tcx.def_map.borrow(); + let a_def = match def_map.get().find(&id) { + None => tcx.sess.span_fatal( + ast_ty.span, format!("unbound path {}", + path_to_str(path, tcx.sess.intr()))), + Some(&d) => d + }; + match a_def { + ast::DefPrimTy(nty) => { + match nty { + ast::TyBool => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_bool()) + } + ast::TyChar => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_char()) + } + ast::TyInt(it) => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_mach_int(it)) + } + ast::TyUint(uit) => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_mach_uint(uit)) + } + ast::TyFloat(ft) => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + Some(ty::mk_mach_float(ft)) + } + ast::TyStr => { + tcx.sess.span_err(ast_ty.span, + "bare `str` is not a type"); + // return /something/ so they can at least get more errors + Some(ty::mk_str(tcx, ty::vstore_uniq)) + } + } + } + _ => None + } + } + _ => None + } +} + // Parses the programmer's textual representation of a type into our // internal notion of a type. pub fn ast_ty_to_ty( @@ -361,26 +429,6 @@ pub fn ast_ty_to_ty( return constr(seq_ty); } - fn check_path_args(tcx: ty::ctxt, - path: &ast::Path, - flags: uint) { - if (flags & NO_TPS) != 0u { - if !path.segments.iter().all(|s| s.types.is_empty()) { - tcx.sess.span_err( - path.span, - "type parameters are not allowed on this type"); - } - } - - if (flags & NO_REGIONS) != 0u { - if !path.segments.last().unwrap().lifetimes.is_empty() { - tcx.sess.span_err( - path.span, - "region parameters are not allowed on this type"); - } - } - } - let tcx = this.tcx(); { @@ -398,191 +446,164 @@ pub fn ast_ty_to_ty( ast_ty_to_ty_cache.get().insert(ast_ty.id, ty::atttce_unresolved); } - - let typ = match ast_ty.node { - ast::TyNil => ty::mk_nil(), - ast::TyBot => ty::mk_bot(), - ast::TyBox(ty) => { - let mt = ast::MutTy { ty: ty, mutbl: ast::MutImmutable }; - mk_pointer(this, rscope, &mt, ty::vstore_box, - |tmt| ty::mk_box(tcx, tmt.ty)) - } - ast::TyUniq(ty) => { - let mt = ast::MutTy { ty: ty, mutbl: ast::MutImmutable }; - mk_pointer(this, rscope, &mt, ty::vstore_uniq, - |tmt| ty::mk_uniq(tcx, tmt.ty)) - } - ast::TyVec(ty) => { - tcx.sess.span_err(ast_ty.span, "bare `[]` is not a type"); - // return /something/ so they can at least get more errors - ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, ty), ty::vstore_uniq) - } - ast::TyPtr(ref mt) => { - ty::mk_ptr(tcx, ast_mt_to_mt(this, rscope, mt)) - } - ast::TyRptr(ref region, ref mt) => { - let r = opt_ast_region_to_region(this, rscope, ast_ty.span, region); - debug!("ty_rptr r={}", r.repr(this.tcx())); - mk_pointer(this, rscope, mt, ty::vstore_slice(r), - |tmt| ty::mk_rptr(tcx, r, tmt)) - } - ast::TyTup(ref fields) => { - let flds = fields.map(|&t| ast_ty_to_ty(this, rscope, t)); - ty::mk_tup(tcx, flds) - } - ast::TyBareFn(ref bf) => { - if bf.decl.variadic && !bf.abis.is_c() { - tcx.sess.span_err(ast_ty.span, "variadic function must have C calling convention"); - } - ty::mk_bare_fn(tcx, ty_of_bare_fn(this, ast_ty.id, bf.purity, - bf.abis, bf.decl)) - } - ast::TyClosure(ref f) => { - if f.sigil == ast::ManagedSigil { - tcx.sess.span_err(ast_ty.span, - "managed closures are not supported"); - } - - let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, match f.sigil { - // Use corresponding trait store to figure out default bounds - // if none were specified. - ast::BorrowedSigil => ty::RegionTraitStore(ty::ReEmpty), // dummy region - ast::OwnedSigil => ty::UniqTraitStore, - ast::ManagedSigil => ty::BoxTraitStore, - }); - let fn_decl = ty_of_closure(this, - rscope, - ast_ty.id, - f.sigil, - f.purity, - f.onceness, - bounds, - &f.region, - f.decl, - None, - ast_ty.span); - ty::mk_closure(tcx, fn_decl) - } - ast::TyPath(ref path, ref bounds, id) => { - let def_map = tcx.def_map.borrow(); - let a_def = match def_map.get().find(&id) { - None => tcx.sess.span_fatal( - ast_ty.span, format!("unbound path {}", - path_to_str(path, tcx.sess.intr()))), - Some(&d) => d - }; - // Kind bounds on path types are only supported for traits. - match a_def { - // But don't emit the error if the user meant to do a trait anyway. - ast::DefTrait(..) => { }, - _ if bounds.is_some() => - tcx.sess.span_err(ast_ty.span, - "kind bounds can only be used on trait types"), - _ => { }, - } - match a_def { - ast::DefTrait(_) => { - let path_str = path_to_str(path, tcx.sess.intr()); - tcx.sess.span_err( - ast_ty.span, - format!("reference to trait `{}` where a type is expected; \ - try `@{}`, `~{}`, or `&{}`", - path_str, path_str, path_str, path_str)); - ty::mk_err() - } - ast::DefTy(did) | ast::DefStruct(did) => { - ast_path_to_ty(this, rscope, did, path).ty - } - ast::DefPrimTy(nty) => { - match nty { - ast::TyBool => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_bool() - } - ast::TyChar => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_char() - } - ast::TyInt(it) => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_mach_int(it) - } - ast::TyUint(uit) => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_mach_uint(uit) - } - ast::TyFloat(ft) => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_mach_float(ft) - } - ast::TyStr => { - tcx.sess.span_err(ast_ty.span, - "bare `str` is not a type"); + let typ = ast_ty_to_prim_ty(tcx, ast_ty).unwrap_or_else(|| match ast_ty.node { + ast::TyNil => ty::mk_nil(), + ast::TyBot => ty::mk_bot(), + ast::TyBox(ty) => { + let mt = ast::MutTy { ty: ty, mutbl: ast::MutImmutable }; + mk_pointer(this, rscope, &mt, ty::vstore_box, + |tmt| ty::mk_box(tcx, tmt.ty)) + } + ast::TyUniq(ty) => { + let mt = ast::MutTy { ty: ty, mutbl: ast::MutImmutable }; + mk_pointer(this, rscope, &mt, ty::vstore_uniq, + |tmt| ty::mk_uniq(tcx, tmt.ty)) + } + ast::TyVec(ty) => { + tcx.sess.span_err(ast_ty.span, "bare `[]` is not a type"); // return /something/ so they can at least get more errors - ty::mk_str(tcx, ty::vstore_uniq) - } + ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, ty), ty::vstore_uniq) } - } - ast::DefTyParam(id, n) => { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_param(tcx, n, id) - } - ast::DefSelfTy(id) => { - // n.b.: resolve guarantees that the this type only appears in a - // trait, which we rely upon in various places when creating - // substs - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - let did = ast_util::local_def(id); - ty::mk_self(tcx, did) - } - ast::DefMod(id) => { - tcx.sess.span_fatal(ast_ty.span, - format!("found module name used as a type: {}", - ast_map::node_id_to_str(tcx.items, id.node, - token::get_ident_interner()))); - } - _ => { - tcx.sess.span_fatal(ast_ty.span, - format!("found value name used as a type: {:?}", a_def)); - } - } - } - ast::TyFixedLengthVec(ty, e) => { - match const_eval::eval_const_expr_partial(&tcx, e) { - Ok(ref r) => { - match *r { - const_eval::const_int(i) => - ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, ty), - ty::vstore_fixed(i as uint)), - const_eval::const_uint(i) => - ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, ty), - ty::vstore_fixed(i as uint)), - _ => { - tcx.sess.span_fatal( - ast_ty.span, "expected constant expr for vector length"); - } + ast::TyPtr(ref mt) => { + ty::mk_ptr(tcx, ast_mt_to_mt(this, rscope, mt)) } - } - Err(ref r) => { - tcx.sess.span_fatal( - ast_ty.span, - format!("expected constant expr for vector length: {}", *r)); - } - } - } - ast::TyTypeof(_e) => { - tcx.sess.span_bug(ast_ty.span, "typeof is reserved but unimplemented"); - } - ast::TyInfer => { - // ty_infer should only appear as the type of arguments or return - // values in a fn_expr, or as the type of local variables. Both of - // these cases are handled specially and should not descend into this - // routine. - this.tcx().sess.span_bug( - ast_ty.span, - "found `ty_infer` in unexpected place"); - } - }; + ast::TyRptr(ref region, ref mt) => { + let r = opt_ast_region_to_region(this, rscope, ast_ty.span, region); + debug!("ty_rptr r={}", r.repr(this.tcx())); + mk_pointer(this, rscope, mt, ty::vstore_slice(r), + |tmt| ty::mk_rptr(tcx, r, tmt)) + } + ast::TyTup(ref fields) => { + let flds = fields.map(|&t| ast_ty_to_ty(this, rscope, t)); + ty::mk_tup(tcx, flds) + } + ast::TyBareFn(ref bf) => { + if bf.decl.variadic && !bf.abis.is_c() { + tcx.sess.span_err(ast_ty.span, + "variadic function must have C calling convention"); + } + ty::mk_bare_fn(tcx, ty_of_bare_fn(this, ast_ty.id, bf.purity, + bf.abis, bf.decl)) + } + ast::TyClosure(ref f) => { + if f.sigil == ast::ManagedSigil { + tcx.sess.span_err(ast_ty.span, + "managed closures are not supported"); + } + + let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, match f.sigil { + // Use corresponding trait store to figure out default bounds + // if none were specified. + ast::BorrowedSigil => ty::RegionTraitStore(ty::ReEmpty), // dummy region + ast::OwnedSigil => ty::UniqTraitStore, + ast::ManagedSigil => ty::BoxTraitStore, + }); + let fn_decl = ty_of_closure(this, + rscope, + ast_ty.id, + f.sigil, + f.purity, + f.onceness, + bounds, + &f.region, + f.decl, + None, + ast_ty.span); + ty::mk_closure(tcx, fn_decl) + } + ast::TyPath(ref path, ref bounds, id) => { + let def_map = tcx.def_map.borrow(); + let a_def = match def_map.get().find(&id) { + None => tcx.sess.span_fatal( + ast_ty.span, format!("unbound path {}", + path_to_str(path, tcx.sess.intr()))), + Some(&d) => d + }; + // Kind bounds on path types are only supported for traits. + match a_def { + // But don't emit the error if the user meant to do a trait anyway. + ast::DefTrait(..) => { }, + _ if bounds.is_some() => + tcx.sess.span_err(ast_ty.span, + "kind bounds can only be used on trait types"), + _ => { }, + } + match a_def { + ast::DefTrait(_) => { + let path_str = path_to_str(path, tcx.sess.intr()); + tcx.sess.span_err( + ast_ty.span, + format!("reference to trait `{}` where a type is expected; \ + try `@{}`, `~{}`, or `&{}`", + path_str, path_str, path_str, path_str)); + ty::mk_err() + } + ast::DefTy(did) | ast::DefStruct(did) => { + ast_path_to_ty(this, rscope, did, path).ty + } + ast::DefTyParam(id, n) => { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + ty::mk_param(tcx, n, id) + } + ast::DefSelfTy(id) => { + // n.b.: resolve guarantees that the this type only appears in a + // trait, which we rely upon in various places when creating + // substs + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + let did = ast_util::local_def(id); + ty::mk_self(tcx, did) + } + ast::DefMod(id) => { + tcx.sess.span_fatal(ast_ty.span, + format!("found module name used as a type: {}", + ast_map::node_id_to_str(tcx.items, id.node, + token::get_ident_interner()))); + } + ast::DefPrimTy(_) => { + fail!("DefPrimTy arm missed in previous ast_ty_to_prim_ty call"); + } + _ => { + tcx.sess.span_fatal(ast_ty.span, + format!("found value name used as a type: {:?}", a_def)); + } + } + } + ast::TyFixedLengthVec(ty, e) => { + match const_eval::eval_const_expr_partial(&tcx, e) { + Ok(ref r) => { + match *r { + const_eval::const_int(i) => + ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, ty), + ty::vstore_fixed(i as uint)), + const_eval::const_uint(i) => + ty::mk_vec(tcx, ast_ty_to_mt(this, rscope, ty), + ty::vstore_fixed(i as uint)), + _ => { + tcx.sess.span_fatal( + ast_ty.span, "expected constant expr for vector length"); + } + } + } + Err(ref r) => { + tcx.sess.span_fatal( + ast_ty.span, + format!("expected constant expr for vector length: {}", *r)); + } + } + } + ast::TyTypeof(_e) => { + tcx.sess.span_bug(ast_ty.span, "typeof is reserved but unimplemented"); + } + ast::TyInfer => { + // ty_infer should only appear as the type of arguments or return + // values in a fn_expr, or as the type of local variables. Both of + // these cases are handled specially and should not descend into this + // routine. + this.tcx().sess.span_bug( + ast_ty.span, + "found `ty_infer` in unexpected place"); + } + }); let mut ast_ty_to_ty_cache = tcx.ast_ty_to_ty_cache.borrow_mut(); ast_ty_to_ty_cache.get().insert(ast_ty.id, ty::atttce_resolved(typ)); diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index a0b50f65f20..64fe6ed20da 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -4285,4 +4285,3 @@ pub fn check_intrinsic_type(ccx: @CrateCtxt, it: &ast::ForeignItem) { ppaux::ty_to_str(ccx.tcx, fty))); } } - diff --git a/src/test/run-pass/issue-5900.rs b/src/test/run-pass/issue-5900.rs new file mode 100644 index 00000000000..4518b503c0d --- /dev/null +++ b/src/test/run-pass/issue-5900.rs @@ -0,0 +1,21 @@ +// 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. + +pub mod foo { + use super::Bar; + + pub struct FooStruct { bar : Bar } +} + +pub enum Bar { + Bar0 = 0 as int +} + +pub fn main() {} diff --git a/src/test/run-pass/issue-9942.rs b/src/test/run-pass/issue-9942.rs new file mode 100644 index 00000000000..7864f4fbdd6 --- /dev/null +++ b/src/test/run-pass/issue-9942.rs @@ -0,0 +1,13 @@ +// 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. + +pub fn main() { + static S: uint = 23 as uint; [0, ..S]; () +}