From 47f1d6747c01a36534ebd08bab497e7abd02c7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Sun, 8 Mar 2015 16:41:28 +0100 Subject: [PATCH 1/2] First-class struct and tuple constants Fixes #23260 --- src/librustc/middle/const_eval.rs | 81 ++++++++++++++++++++++--------- src/librustc/middle/ty.rs | 17 +++---- 2 files changed, 64 insertions(+), 34 deletions(-) diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index f215b59a6cd0e..96433729a9b96 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -17,7 +17,7 @@ use metadata::csearch; use middle::{astencode, def}; use middle::pat_util::def_to_path; use middle::ty::{self, Ty}; -use middle::astconv_util::{ast_ty_to_prim_ty}; +use middle::astconv_util::ast_ty_to_prim_ty; use syntax::ast::{self, Expr}; use syntax::codemap::Span; @@ -132,16 +132,16 @@ pub fn lookup_const_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId) } } -// FIXME (#33): this doesn't handle big integer/float literals correctly -// (nor does the rest of our literal handling). #[derive(Clone, PartialEq)] pub enum const_val { const_float(f64), const_int(i64), const_uint(u64), const_str(InternedString), - const_binary(Rc >), - const_bool(bool) + const_binary(Rc>), + const_bool(bool), + Struct(ast::NodeId), + Tuple(ast::NodeId) } pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P { @@ -226,9 +226,13 @@ pub enum ErrKind { NegateOnString, NegateOnBoolean, NegateOnBinary, + NegateOnStruct, + NegateOnTuple, NotOnFloat, NotOnString, NotOnBinary, + NotOnStruct, + NotOnTuple, AddiWithOverflow(i64, i64), SubiWithOverflow(i64, i64), @@ -242,7 +246,8 @@ pub enum ErrKind { ModuloWithOverflow, MissingStructField, NonConstPath, - NonConstStruct, + ExpectedConstTuple, + ExpectedConstStruct, TupleIndexOutOfBounds, MiscBinaryOp, @@ -262,9 +267,13 @@ impl ConstEvalErr { NegateOnString => "negate on string".into_cow(), NegateOnBoolean => "negate on boolean".into_cow(), NegateOnBinary => "negate on binary literal".into_cow(), + NegateOnStruct => "negate on struct".into_cow(), + NegateOnTuple => "negate on tuple".into_cow(), NotOnFloat => "not on float or string".into_cow(), NotOnString => "not on float or string".into_cow(), NotOnBinary => "not on binary literal".into_cow(), + NotOnStruct => "not on struct".into_cow(), + NotOnTuple => "not on tuple".into_cow(), AddiWithOverflow(..) => "attempted to add with overflow".into_cow(), SubiWithOverflow(..) => "attempted to sub with overflow".into_cow(), @@ -278,7 +287,8 @@ impl ConstEvalErr { ModuloWithOverflow => "attempted remainder with overflow".into_cow(), MissingStructField => "nonexistent struct field".into_cow(), NonConstPath => "non-constant path in constant expr".into_cow(), - NonConstStruct => "non-constant struct in constant expr".into_cow(), + ExpectedConstTuple => "expected constant tuple".into_cow(), + ExpectedConstStruct => "expected constant struct".into_cow(), TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(), MiscBinaryOp => "bad operands for binary".into_cow(), @@ -341,6 +351,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, const_str(_) => signal!(e, NegateOnString), const_bool(_) => signal!(e, NegateOnBoolean), const_binary(_) => signal!(e, NegateOnBinary), + const_val::Tuple(_) => signal!(e, NegateOnTuple), + const_val::Struct(..) => signal!(e, NegateOnStruct), } } ast::ExprUnary(ast::UnNot, ref inner) => { @@ -351,6 +363,8 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, const_str(_) => signal!(e, NotOnString), const_float(_) => signal!(e, NotOnFloat), const_binary(_) => signal!(e, NotOnBinary), + const_val::Tuple(_) => signal!(e, NotOnTuple), + const_val::Struct(..) => signal!(e, NotOnStruct), } } ast::ExprBinary(op, ref a, ref b) => { @@ -540,33 +554,52 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, None => const_int(0) } } + ast::ExprTup(_) => { + const_val::Tuple(e.id) + } + ast::ExprStruct(..) => { + const_val::Struct(e.id) + } ast::ExprTupField(ref base, index) => { - // Get the base tuple if it is constant - if let Some(&ast::ExprTup(ref fields)) = lookup_const(tcx, &**base).map(|s| &s.node) { - // Check that the given index is within bounds and evaluate its value - if fields.len() > index.node { - return eval_const_expr_partial(tcx, &*fields[index.node], None); + if let Ok(c) = eval_const_expr_partial(tcx, base, None) { + if let const_val::Tuple(tup_id) = c { + if let ast::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node { + if index.node < fields.len() { + return eval_const_expr_partial(tcx, &fields[index.node], None) + } else { + signal!(e, TupleIndexOutOfBounds); + } + } else { + unreachable!() + } } else { - signal!(e, TupleIndexOutOfBounds); + signal!(base, ExpectedConstTuple); } + } else { + signal!(base, NonConstPath) } - - signal!(e, NonConstStruct); } ast::ExprField(ref base, field_name) => { // Get the base expression if it is a struct and it is constant - if let Some(&ast::ExprStruct(_, ref fields, _)) = lookup_const(tcx, &**base) - .map(|s| &s.node) { - // Check that the given field exists and evaluate it - if let Some(f) = fields.iter().find(|f| - f.ident.node.as_str() == field_name.node.as_str()) { - return eval_const_expr_partial(tcx, &*f.expr, None); + if let Ok(c) = eval_const_expr_partial(tcx, base, None) { + if let const_val::Struct(struct_id) = c { + if let ast::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node { + // Check that the given field exists and evaluate it + if let Some(f) = fields.iter().find(|f| f.ident.node.as_str() + == field_name.node.as_str()) { + return eval_const_expr_partial(tcx, &*f.expr, None) + } else { + signal!(e, MissingStructField); + } + } else { + unreachable!() + } } else { - signal!(e, MissingStructField); + signal!(base, ExpectedConstStruct); } + } else { + signal!(base, NonConstPath); } - - signal!(e, NonConstStruct); } _ => signal!(e, MiscCatchAll) }; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 4cb4d343de758..a871602b86519 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5853,16 +5853,13 @@ pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> uint { let found = match val { const_eval::const_uint(count) => return count as uint, const_eval::const_int(count) if count >= 0 => return count as uint, - const_eval::const_int(_) => - "negative integer", - const_eval::const_float(_) => - "float", - const_eval::const_str(_) => - "string", - const_eval::const_bool(_) => - "boolean", - const_eval::const_binary(_) => - "binary array" + const_eval::const_int(_) => "negative integer", + const_eval::const_float(_) => "float", + const_eval::const_str(_) => "string", + const_eval::const_bool(_) => "boolean", + const_eval::const_binary(_) => "binary array", + const_eval::Struct(..) => "struct", + const_eval::Tuple(_) => "tuple" }; span_err!(tcx.sess, count_expr.span, E0306, "expected positive integer for repeat count, found {}", From a83db812385f9bf281078c7c8ede44f6863f2966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Ochagav=C3=ADa?= Date: Wed, 11 Mar 2015 10:57:12 +0100 Subject: [PATCH 2/2] Update tests --- src/test/compile-fail/repeat_count.rs | 2 +- src/test/run-pass/issue-19244.rs | 29 +++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/test/compile-fail/repeat_count.rs b/src/test/compile-fail/repeat_count.rs index 9b3e2668042ea..121581412202c 100644 --- a/src/test/compile-fail/repeat_count.rs +++ b/src/test/compile-fail/repeat_count.rs @@ -19,7 +19,7 @@ fn main() { //~| found `()` //~| expected usize //~| found () -//~| ERROR expected constant integer for repeat count, found non-constant expression +//~| ERROR expected positive integer for repeat count, found tuple let c = [0; true]; //~^ ERROR mismatched types //~| expected `usize` diff --git a/src/test/run-pass/issue-19244.rs b/src/test/run-pass/issue-19244.rs index 9af4d30c4f640..35e053110dfc7 100644 --- a/src/test/run-pass/issue-19244.rs +++ b/src/test/run-pass/issue-19244.rs @@ -8,14 +8,35 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct MyStruct { field: uint } +struct MyStruct { field: usize } +struct Nested { nested: MyStruct } +struct Mix2 { nested: ((usize,),) } + const STRUCT: MyStruct = MyStruct { field: 42 }; -const TUP: (uint,) = (43,); +const TUP: (usize,) = (43,); +const NESTED_S: Nested = Nested { nested: MyStruct { field: 5 } }; +const NESTED_T: ((usize,),) = ((4,),); +const MIX_1: ((Nested,),) = ((Nested { nested: MyStruct { field: 3 } },),); +const MIX_2: Mix2 = Mix2 { nested: ((2,),) }; +const INSTANT_1: usize = (MyStruct { field: 1 }).field; +const INSTANT_2: usize = (0,).0; fn main() { let a = [0; STRUCT.field]; let b = [0; TUP.0]; + let c = [0; NESTED_S.nested.field]; + let d = [0; (NESTED_T.0).0]; + let e = [0; (MIX_1.0).0.nested.field]; + let f = [0; (MIX_2.nested.0).0]; + let g = [0; INSTANT_1]; + let h = [0; INSTANT_2]; - assert!(a.len() == 42); - assert!(b.len() == 43); + assert_eq!(a.len(), 42); + assert_eq!(b.len(), 43); + assert_eq!(c.len(), 5); + assert_eq!(d.len(), 4); + assert_eq!(e.len(), 3); + assert_eq!(f.len(), 2); + assert_eq!(g.len(), 1); + assert_eq!(h.len(), 0); }