diff --git a/languages/source.nim b/languages/source.nim index 7ad2b8ba..0837fdb6 100644 --- a/languages/source.nim +++ b/languages/source.nim @@ -29,6 +29,7 @@ const lang* = language: Call(+expr) FieldAccess(expr, IntVal(z)) At(expr, expr) + As(expr, texpr) And(expr, expr) Or(expr, expr) If(expr, expr, expr) @@ -124,6 +125,8 @@ const lang* = language: FieldAccess(xfrm(e_1), IntVal(z_1)) of At(e_1, e_2): At(xfrm(e_1), xfrm(e_2)) + of As(e_1, texpr_1): + As(xfrm(e_1), texpr_1) of While(e_1, e_2): While(xfrm(e_1), xfrm(e_2)) of Return(e_1): @@ -350,6 +353,13 @@ const lang* = language: where SeqTy(typ_2), typ_1 conclusion C_1, At(e_1, e_2), mut(typ_2) + rule "S-as": + premise mtypes(C_1, e_1, typ_1) + premise ttypes(C_1, texpr_1, typ_2) + condition typ_2 != VoidTy() + condition typ_1 <:= typ_2 + conclusion C_1, As(e_1, texpr_1), typ_2 + rule "S-asgn": premise types(C_1, e_1, mut(typ_1)) premise mtypes(C_1, e_2, typ_2) @@ -575,6 +585,8 @@ const lang* = language: FieldAccess(substitute(e_1, with), IntVal(z_1)) of At(e_1, e_2): At(substitute(e_1, with), substitute(e_2, with)) + of As(e_1, texpr_1): + As(substitute(e_1, with), texpr_1) of While(e_1, e_2): While(substitute(e_1, with), substitute(e_2, with)) of Return(e_1): @@ -696,6 +708,7 @@ const lang* = language: At(E, e) At(le, E) At(val, E) + As(E, texpr) Asgn(E, e) Asgn(le, E) With(E, n, e) @@ -715,6 +728,7 @@ const lang* = language: Exprs(hole, +e) At(le, hole) At(val, hole) + As(hole, texpr) Asgn(le, hole) With(hole, n, e) With(val, n, hole) @@ -744,6 +758,7 @@ const lang* = language: axiom "E-exprs", Exprs(TupleCons(), +e_1), Exprs(...e_1) axiom "E-if-true", If(True, e_1, e_2), e_1 axiom "E-if-false", If(False, e_1, e_2), e_2 + axiom "E-as", As(val_1, texpr), val_1 # a no-op axiom "E-while", While(e_1, e_2), If(e_1, Exprs(e_2, While(e_1, e_2)), TupleCons()) diff --git a/languages/specification.md b/languages/specification.md index eab11f18..8c525560 100644 --- a/languages/specification.md +++ b/languages/specification.md @@ -20,6 +20,7 @@ expr ::= | (Call +) | (FieldAccess ) | (At ) + | (As ) | (And ) | (Or ) | (If ?) diff --git a/passes/source2il.nim b/passes/source2il.nim index 491e5413..6dd39bc6 100644 --- a/passes/source2il.nim +++ b/passes/source2il.nim @@ -697,7 +697,11 @@ proc fitExpr(c; e: sink Expr, target: SemType): Expr = else: # TODO: this needs a better error message c.error("type mismatch") - result = Expr(stmts: e.stmts, typ: errorType()) + # still return a proper expression so that analysis can carry on + if target.kind == tkVoid: + result = Expr(typ: target, stmts: e.stmts) + else: + result = Expr(typ: target, stmts: e.stmts, expr: e.expr) proc fitExprStrict(c; e: sink Expr, typ: SemType): Expr = ## Makes sure expression `e` fits `typ` exactly, reporting an error and @@ -1274,6 +1278,17 @@ proc exprToIL(c; t: InTree, n: NodeIndex, expr, stmts): ExprType = newCall(at, @[c.inlineLvalue(arr, stmts), c.capture(idx, stmts)])) else: result = errorType() + arr.attribs + of SourceKind.As: + let + (a, b) = t.pair(n) + e = c.exprToIL(t, a) + typ = c.expectNot(c.evalType(t, b), tkVoid) + # a copy must always be created, even when there's no widening going on + if e.typ == typ: + expr = use(c, e, stmts) + else: + expr = inline(c.fitExpr(e, typ), stmts) + result = typ + {} # lvalue-ness and mutability are discarded of SourceKind.Asgn: let (a, b) = t.pair(n) var dst = c.exprToIL(t, a) diff --git a/passes/syntax_source.nim b/passes/syntax_source.nim index 84abe987..109cd66d 100644 --- a/passes/syntax_source.nim +++ b/passes/syntax_source.nim @@ -20,6 +20,7 @@ type TupleCons Seq FieldAccess, At + As Exprs Asgn Return @@ -34,7 +35,8 @@ type const ExprNodes* = {IntVal, FloatVal, Ident, And, Or, If, While, Call, TupleCons, - Seq, FieldAccess, At, Asgn, Return, Unreachable, Exprs, Decl} + Seq, FieldAccess, At, As, Asgn, Return, Unreachable, Exprs, + Decl} DeclNodes* = {ProcDecl, TypeDecl} AllNodes* = {low(NodeKind) .. high(NodeKind)} diff --git a/tests/expr/spectest.nim b/tests/expr/spectest.nim index 8fcdd233..53b55748 100644 --- a/tests/expr/spectest.nim +++ b/tests/expr/spectest.nim @@ -57,9 +57,6 @@ const "t05_proc_with_union_return_type.test", "t05_return_operand_cannot_be_void.test", "t05_return_type_mismatch.test", - "t05_union_type_1.test", - "t05_union_type_2.test", - "t05_union_type_of_single_type.test", "t05_unreachable_is_void.test", "t06_call_lookup_error.test", "t06_call_lookup_self_visible.test", @@ -75,10 +72,6 @@ const "t06_redeclaration_error_1.test", "t06_redeclaration_error_2.test", "t06_redeclaration_error_3.test", - "t06_union_duplicate_types.test", - "t08_if_expr_unify_type_1.test", - "t08_if_expr_unify_type_2.test", - "t09_asgn_compatible_type.test", "t09_asgn_field.test", "t09_decl_void_error.test", "t10_asgn_lhs_must_be_lvalue_8.test", @@ -116,7 +109,6 @@ const "t17_seq_copy_2.test", "t17_seq_copy_3.test", "t17_seq_copy_4.test", - "t17_seq_nested_copy_3.test", "t18_seq_character_string_1.test", "t18_seq_character_string_2.test", "t19_write.test", diff --git a/tests/expr/t02_as.test b/tests/expr/t02_as.test new file mode 100644 index 00000000..c90e4b68 --- /dev/null +++ b/tests/expr/t02_as.test @@ -0,0 +1,4 @@ +discard """ + output: "1 : (IntTy)" +""" +(As 1 (IntTy)) diff --git a/tests/expr/t02_as_no_void_error.test b/tests/expr/t02_as_no_void_error.test new file mode 100644 index 00000000..8eb586fe --- /dev/null +++ b/tests/expr/t02_as_no_void_error.test @@ -0,0 +1,4 @@ +discard """ + reject: true +""" +(As 1 (VoidTy)) diff --git a/tests/expr/t02_as_type_mismatch_error.test b/tests/expr/t02_as_type_mismatch_error.test new file mode 100644 index 00000000..9c60978a --- /dev/null +++ b/tests/expr/t02_as_type_mismatch_error.test @@ -0,0 +1,4 @@ +discard """ + reject: true +""" +(As 1 (UnitTy)) diff --git a/tests/expr/t05_union_type_1.test b/tests/expr/t05_union_type_1.test index 627d523d..1319b0d4 100644 --- a/tests/expr/t05_union_type_1.test +++ b/tests/expr/t05_union_type_1.test @@ -1,5 +1,4 @@ discard """ output: "100 : (UnionTy (IntTy) (FloatTy))" """ -(ProcDecl (Ident "a") (UnionTy (IntTy) (FloatTy)) (Params) - (Return (IntVal 100))) +(As 100 (UnionTy (IntTy) (FloatTy))) diff --git a/tests/expr/t05_union_type_2.test b/tests/expr/t05_union_type_2.test index 2090c2d9..65b02654 100644 --- a/tests/expr/t05_union_type_2.test +++ b/tests/expr/t05_union_type_2.test @@ -1,5 +1,4 @@ discard """ output: "1.5 : (UnionTy (IntTy) (FloatTy))" """ -(ProcDecl (Ident "a") (UnionTy (IntTy) (FloatTy)) (Params) - (Return (FloatVal 1.5))) +(As 1.5 (UnionTy (IntTy) (FloatTy))) \ No newline at end of file diff --git a/tests/expr/t05_union_type_of_single_type.test b/tests/expr/t05_union_type_of_single_type.test index 7df926e8..b22ddb77 100644 --- a/tests/expr/t05_union_type_of_single_type.test +++ b/tests/expr/t05_union_type_of_single_type.test @@ -1,5 +1,4 @@ discard """ output: "100 : (UnionTy (IntTy))" """ -(ProcDecl (Ident "a") (UnionTy (IntTy)) (Params) - (Return (IntVal 100))) +(As 100 (UnionTy (IntTy))) diff --git a/tests/expr/t06_union_duplicate_types.test b/tests/expr/t06_union_duplicate_types.test index a3e1f445..07b4bff8 100644 --- a/tests/expr/t06_union_duplicate_types.test +++ b/tests/expr/t06_union_duplicate_types.test @@ -1,5 +1,4 @@ discard """ reject: true """ -(ProcDecl (Ident "a") (UnionTy (IntTy) (IntTy)) (Params) - (Return (IntVal 100))) \ No newline at end of file +(As 100 (UnionTy (IntTy) (IntTy))) diff --git a/tests/expr/t08_if_expr_unify_type_1.test b/tests/expr/t08_if_expr_unify_type_1.test index b2a9c01a..13bcde44 100644 --- a/tests/expr/t08_if_expr_unify_type_1.test +++ b/tests/expr/t08_if_expr_unify_type_1.test @@ -1,13 +1,7 @@ discard """ description: "Ensure types unify for an if-expression" + output: "0.5 : (UnionTy (IntTy) (FloatTy))" """ -(Module - (ProcDecl (Ident "p") (UnionTy (IntTy) (FloatTy)) (Params) - (Return (IntVal 100))) - - (ProcDecl (Ident "test") (UnionTy (IntTy) (FloatTy)) (Params) - (Return - (If (Ident "true") - (FloatVal 0.5) - (Call (Ident "p"))))) -) \ No newline at end of file +(If (Ident "true") + (FloatVal 0.5) + (As (IntVal 100) (UnionTy (IntTy) (FloatTy))))) diff --git a/tests/expr/t08_if_expr_unify_type_2.test b/tests/expr/t08_if_expr_unify_type_2.test index 84b01fc7..89af6497 100644 --- a/tests/expr/t08_if_expr_unify_type_2.test +++ b/tests/expr/t08_if_expr_unify_type_2.test @@ -1,13 +1,7 @@ discard """ description: "Ensure types unify for an if-expression" + output: "0.5 : (UnionTy (IntTy) (FloatTy))" """ -(Module - (ProcDecl (Ident "p") (UnionTy (IntTy) (FloatTy)) (Params) - (Return (IntVal 100))) - - (ProcDecl (Ident "test") (UnionTy (IntTy) (FloatTy)) (Params) - (Return - (If (Ident "false") - (Call (Ident "p")) - (FloatVal 0.5)))) -) \ No newline at end of file +(If (Ident "false") + (As (IntVal 100) (UnionTy (IntTy) (FloatTy))) + (FloatVal 0.5)))) \ No newline at end of file diff --git a/tests/expr/t09_asgn_compatible_type.test b/tests/expr/t09_asgn_compatible_type.test index a0f74f36..177017a1 100644 --- a/tests/expr/t09_asgn_compatible_type.test +++ b/tests/expr/t09_asgn_compatible_type.test @@ -1,12 +1,8 @@ discard """ output: "1.5 : (UnionTy (IntTy) (FloatTy))" """ -(Module - (ProcDecl (Ident "union") (UnionTy (IntTy) (FloatTy)) (Params) - (Return (IntVal 100))) - - (ProcDecl (Ident "test") (UnionTy (IntTy) (FloatTy)) (Params) - (Exprs - (Decl (Ident "x") (Call (Ident "union"))) - (Asgn (Ident "x") (FloatVal 1.5)) - (Return (Ident "x"))))) +(Exprs + (Decl (Ident "x") + (As (IntVal 100) (UnionTy (IntTy) (FloatTy)))) + (Asgn (Ident "x") (FloatVal 1.5)) + (Ident "x")) diff --git a/tests/expr/t10_asgn_lhs_must_be_lvalue_11_error.test b/tests/expr/t10_asgn_lhs_must_be_lvalue_11_error.test new file mode 100644 index 00000000..023cb640 --- /dev/null +++ b/tests/expr/t10_asgn_lhs_must_be_lvalue_11_error.test @@ -0,0 +1,7 @@ +discard """ + description: "An `As` expression is not an l-value expression" + reject: true +""" +(Exprs + (Decl x 1) + (Asgn (As x (IntTy)) 4)) diff --git a/tests/expr/t12_as_copies.test b/tests/expr/t12_as_copies.test new file mode 100644 index 00000000..08bd4706 --- /dev/null +++ b/tests/expr/t12_as_copies.test @@ -0,0 +1,14 @@ +discard """ + description: " + `As` is not an lvalue expression. The sub-expression is fully evaluated + early. + " + output: "1 : (IntTy)" +""" +(Exprs + (Decl x (Seq (IntTy) 1)) + (At + (As x (SeqTy (IntTy))) + (Exprs + (Asgn x (Seq (IntTy) 2)) + 0))) diff --git a/tests/expr/t17_seq_copy_3.test b/tests/expr/t17_seq_copy_3.test index 339a2274..093c6db2 100644 --- a/tests/expr/t17_seq_copy_3.test +++ b/tests/expr/t17_seq_copy_3.test @@ -1,13 +1,10 @@ discard """ output: "(array 1 2 3) : (UnionTy (IntTy) (SeqTy (IntTy)))" """ -(Module - (ProcDecl union (UnionTy (IntTy) (SeqTy (IntTy))) (Params) - (Return 1)) - (ProcDecl main (UnionTy (IntTy) (SeqTy (IntTy))) (Params) - (Exprs - (Decl x (Seq (IntTy) 1 2 3)) - (Decl y (Call union)) - (Asgn y x) - (Asgn (At x 0) 4) - (Return y)))) +(Exprs + (Decl x (Seq (IntTy) 1 2 3)) + (Decl y + (As 1 (UnionTy (IntTy) (SeqTy (IntTy))))) + (Asgn y x) + (Asgn (At x 0) 4) + y) diff --git a/tests/expr/t17_seq_nested_copy_3.test b/tests/expr/t17_seq_nested_copy_3.test index 5412ac1a..121a2db9 100644 --- a/tests/expr/t17_seq_nested_copy_3.test +++ b/tests/expr/t17_seq_nested_copy_3.test @@ -2,11 +2,9 @@ discard """ description: "Sequences part of unions are fully copied when the union is" output: "(array 1 2 3) : (UnionTy (IntTy) (SeqTy (IntTy)))" """ -(Module - (ProcDecl union (UnionTy (IntTy) (SeqTy (IntTy))) (Params) - (Return (Seq (IntTy) 1 2 3))) - (ProcDecl main (UnionTy (IntTy) (SeqTy (IntTy))) (Params) - (Exprs - (Decl x (Call union)) - (Decl y x) - (Return y)))) +(Exprs + (Decl x + (As (Seq (IntTy) 1 2 3) + (UnionTy (IntTy) (SeqTy (IntTy))))) + (Decl y x) + y)