From 4228f0a895b0b2e5d224ed7268f4f328e6767a6b Mon Sep 17 00:00:00 2001 From: Claire Foster Date: Thu, 26 Sep 2024 12:14:24 +1000 Subject: [PATCH] Test all lowering error messages! --- src/desugaring.jl | 2 +- test/assignments.jl | 32 ------------------ test/assignments_ir.jl | 72 ++++++++++++++++++++++++++++++++++++++++ test/branching.jl | 37 ++++++--------------- test/branching_ir.jl | 34 +++++++++++++++++++ test/decls.jl | 24 -------------- test/decls_ir.jl | 49 +++++++++++++++++++++++---- test/destructuring.jl | 7 ---- test/destructuring_ir.jl | 24 ++++++++++++++ test/functions.jl | 10 ------ test/functions_ir.jl | 20 +++++++++++ test/loops.jl | 17 ---------- test/loops_ir.jl | 32 ++++++++++++++++++ test/macros.jl | 25 -------------- test/macros_ir.jl | 50 ++++++++++++++++++++++++++++ test/misc_ir.jl | 39 ++++++++++++++++++++++ test/modules.jl | 14 -------- test/quoting_ir.jl | 45 +++++++++++++++++++++++++ test/utils.jl | 3 ++ 19 files changed, 374 insertions(+), 162 deletions(-) create mode 100644 test/quoting_ir.jl diff --git a/src/desugaring.jl b/src/desugaring.jl index 0b0fe01..84870fa 100644 --- a/src/desugaring.jl +++ b/src/desugaring.jl @@ -318,7 +318,7 @@ function expand_property_destruct(ctx, ex) lhs = ex[1] @assert kind(lhs) == K"tuple" if numchildren(lhs) != 1 - throw(LoweringError(ex, "Property destructuring must use a single `;` before the property names, eg `(; a, b) = rhs`")) + throw(LoweringError(lhs, "Property destructuring must use a single `;` before the property names, eg `(; a, b) = rhs`")) end params = lhs[1] @assert kind(params) == K"parameters" diff --git a/test/assignments.jl b/test/assignments.jl index 8267006..07b4822 100644 --- a/test/assignments.jl +++ b/test/assignments.jl @@ -71,37 +71,5 @@ let end """) === 10 -#------------------------------------------------------------------------------- -# Invalid assignment left hand sides with specific error messages -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -a.(b) = c -""") - -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -T[x y] = z -""") -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -T[x; y] = z -""") -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -T[x ;;; y] = z -""") - -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -[x, y] = z -""") -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -[x y] = z -""") -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -[x; y] = z -""") -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -[x ;;; y] = z -""") - -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -1 = x -""") end diff --git a/test/assignments_ir.jl b/test/assignments_ir.jl index e0d93bf..76f23e7 100644 --- a/test/assignments_ir.jl +++ b/test/assignments_ir.jl @@ -222,3 +222,75 @@ end 3 (call top.setindex! %₂ %₁) 4 (return %₁) +######################################## +# Error: Invalid lhs in `=` +a.(b) = rhs +#--------------------- +LoweringError: +a.(b) = rhs +└───┘ ── invalid dot call syntax on left hand side of assignment + +######################################## +# Error: Invalid lhs in `=` +T[x y] = rhs +#--------------------- +LoweringError: +T[x y] = rhs +└────┘ ── invalid spacing in left side of indexed assignment + +######################################## +# Error: Invalid lhs in `=` +T[x; y] = rhs +#--------------------- +LoweringError: +T[x; y] = rhs +└─────┘ ── unexpected `;` in left side of indexed assignment + +######################################## +# Error: Invalid lhs in `=` +T[x ;;; y] = rhs +#--------------------- +LoweringError: +T[x ;;; y] = rhs +└────────┘ ── unexpected `;` in left side of indexed assignment + +######################################## +# Error: Invalid lhs in `=` +[x, y] = rhs +#--------------------- +LoweringError: +[x, y] = rhs +└────┘ ── use `(a, b) = ...` to assign multiple values + +######################################## +# Error: Invalid lhs in `=` +[x y] = rhs +#--------------------- +LoweringError: +[x y] = rhs +└───┘ ── use `(a, b) = ...` to assign multiple values + +######################################## +# Error: Invalid lhs in `=` +[x; y] = rhs +#--------------------- +LoweringError: +[x; y] = rhs +└────┘ ── use `(a, b) = ...` to assign multiple values + +######################################## +# Error: Invalid lhs in `=` +[x ;;; y] = rhs +#--------------------- +LoweringError: +[x ;;; y] = rhs +└───────┘ ── use `(a, b) = ...` to assign multiple values + +######################################## +# Error: Invalid lhs in `=` +1 = rhs +#--------------------- +LoweringError: +1 = rhs +╙ ── invalid assignment location + diff --git a/test/branching.jl b/test/branching.jl index f040b5f..b3ebcd3 100644 --- a/test/branching.jl +++ b/test/branching.jl @@ -305,36 +305,21 @@ end end @testset "symbolic goto/label" begin - JuliaLowering.include_string(test_mod, """ - let - a = [] - i = 1 - @label foo - push!(a, i) - i = i + 1 - if i <= 2 - @goto foo - end - a - end - """) == [1,2] - @test_throws LoweringError JuliaLowering.include_string(test_mod, """ - begin +JuliaLowering.include_string(test_mod, """ +let + a = [] + i = 1 + @label foo + push!(a, i) + i = i + 1 + if i <= 2 @goto foo end - """) - - @test_throws LoweringError JuliaLowering.include_string(test_mod, """ - begin - @label foo - @label foo - end - """) + a +end +""") == [1,2] - @test_throws LoweringError JuliaLowering.include_string(test_mod, """ - x = @label foo - """) end end diff --git a/test/branching_ir.jl b/test/branching_ir.jl index a817e02..4d92070 100644 --- a/test/branching_ir.jl +++ b/test/branching_ir.jl @@ -202,3 +202,37 @@ end 23 (pop_exception %₁) 24 (return core.nothing) +######################################## +# Error: no symbolic label +begin + @goto foo +end +#--------------------- +LoweringError: +begin + @goto foo +# └─┘ ── label `foo` referenced but not defined +end + +######################################## +# Error: duplicate symbolic label +begin + @label foo + @label foo +end +#--------------------- +LoweringError: +begin + @label foo + @label foo +# └─┘ ── Label `foo` defined multiple times +end + +######################################## +# Error: using value of symbolic label +x = @label foo +#--------------------- +LoweringError: +x = @label foo +# └─┘ ── misplaced label in value position + diff --git a/test/decls.jl b/test/decls.jl index dd70ef4..08484df 100644 --- a/test/decls.jl +++ b/test/decls.jl @@ -49,28 +49,4 @@ end @test Core.get_binding_type(test_mod, :a_typed_global_2) === Int @test test_mod.a_typed_global_2 === 10 -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -begin - local x::T = 1 - local x::S = 1 -end -""") - -# Const not supported on locals -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -const local x = 1 -""") -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -let - const x = 1 -end -""") - -# global type decls only allowed at top level -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -function f() - global x::Int = 1 -end -""") - end diff --git a/test/decls_ir.jl b/test/decls_ir.jl index eb4c10a..9648b3a 100644 --- a/test/decls_ir.jl +++ b/test/decls_ir.jl @@ -80,11 +80,48 @@ global xx::T = 10 14 (return 10) ######################################## -# Type assert (TODO: move this?) -x::T +# Error: x declared twice +begin + local x::T = 1 + local x::S = 1 +end #--------------------- -1 TestMod.x -2 TestMod.T -3 (call core.typeassert %₁ %₂) -4 (return %₃) +LoweringError: +begin + local x::T = 1 + local x::S = 1 +# └──┘ ── multiple type declarations found for `x` +end + +######################################## +# Error: Const not supported on locals +const local x = 1 +#--------------------- +LoweringError: +const local x = 1 +# ╙ ── unsupported `const` declaration on local variable + +######################################## +# Error: Const not supported on locals +let + const x = 1 +end +#--------------------- +LoweringError: +let + const x = 1 +# ╙ ── unsupported `const` declaration on local variable +end + +######################################## +# Error: global type decls only allowed at top level +function f() + global x::Int = 1 +end +#--------------------- +LoweringError: +function f() + global x::Int = 1 +# └────┘ ── type declarations for global variables must be at top level, not inside a function +end diff --git a/test/destructuring.jl b/test/destructuring.jl index d5f5d34..7e5aac5 100644 --- a/test/destructuring.jl +++ b/test/destructuring.jl @@ -89,10 +89,6 @@ let end """) == (1, [2,3], 4) -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -(xs..., ys...) = x -""") - end @@ -138,9 +134,6 @@ let end """) == (1, 2) -@test_throws LoweringError JuliaLowering.include_string(test_mod, "(x ; a, b) = rhs") -@test_throws LoweringError JuliaLowering.include_string(test_mod, "(; a=1, b) = rhs") - end end diff --git a/test/destructuring_ir.jl b/test/destructuring_ir.jl index d9b50c0..d7d110f 100644 --- a/test/destructuring_ir.jl +++ b/test/destructuring_ir.jl @@ -81,6 +81,14 @@ end 12 TestMod.as 13 (return %₁₂) +######################################## +# Error: Slurping multiple args +(xs..., ys...) = x +#--------------------- +LoweringError: +(xs..., ys...) = x +# └────┘ ── multiple `...` in destructuring assignment are ambiguous + ######################################## # Recursive destructuring let @@ -255,3 +263,19 @@ end 16 TestMod.rhs 17 (return %₁₆) +######################################## +# Error: Property destructuring with frankentuple +(x ; a, b) = rhs +#--------------------- +LoweringError: +(x ; a, b) = rhs +└────────┘ ── Property destructuring must use a single `;` before the property names, eg `(; a, b) = rhs` + +######################################## +# Error: Property destructuring with values for properties +(; a=1, b) = rhs +#--------------------- +LoweringError: +(; a=1, b) = rhs +# └─┘ ── invalid assignment location + diff --git a/test/functions.jl b/test/functions.jl index ace1163..b620b3f 100644 --- a/test/functions.jl +++ b/test/functions.jl @@ -45,16 +45,6 @@ begin end """) === (42, 255) -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -function ccall() -end -""") - -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -function A.ccall() -end -""") - Base.include_string(test_mod, """ struct X end diff --git a/test/functions_ir.jl b/test/functions_ir.jl index 554598e..daa4847 100644 --- a/test/functions_ir.jl +++ b/test/functions_ir.jl @@ -42,3 +42,23 @@ end 1 (return core.nothing) 8 (return %₂) +######################################## +# Error: Invalid function name +function ccall() +end +#--------------------- +LoweringError: +function ccall() +# └───┘ ── Invalid function name +end + +######################################## +# Error: Invalid function name +function A.ccall() +end +#--------------------- +LoweringError: +function A.ccall() +# └─────┘ ── Invalid function name +end + diff --git a/test/loops.jl b/test/loops.jl index 4e1dd8d..efa9a9b 100644 --- a/test/loops.jl +++ b/test/loops.jl @@ -45,14 +45,6 @@ let end """) == [2,4] -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -break -""") - -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -continue -""") - # TODO: Test soft scope rules end @@ -152,15 +144,6 @@ let end """) == 2 -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -let - for outer i = 1:2 - nothing - end - i -end -""") - end diff --git a/test/loops_ir.jl b/test/loops_ir.jl index d3c244a..6abaa48 100644 --- a/test/loops_ir.jl +++ b/test/loops_ir.jl @@ -72,3 +72,35 @@ end 16 (goto label₇) 17 (return core.nothing) +######################################## +# Error: break outside for/while +break +#--------------------- +LoweringError: +break +└───┘ ── break must be used inside a `while` or `for` loop + +######################################## +# Error: continue outside for/while +continue +#--------------------- +LoweringError: +continue +└──────┘ ── continue must be used inside a `while` or `for` loop + +######################################## +# Error: `outer` without outer local variable +let + for outer i = 1:2 + nothing + end + i +end +#--------------------- +LoweringError: +let + for outer i = 1:2 +# ╙ ── `outer` annotations must match with a local variable in an outer scope but no such variable was found + nothing + end + diff --git a/test/macros.jl b/test/macros.jl index 2ecd666..2338e0e 100644 --- a/test/macros.jl +++ b/test/macros.jl @@ -142,29 +142,4 @@ end == [ "2" ] - -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -macro mmm(a; b=2) -end -""") - -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -macro mmm[](ex) -end -""") - -# Macros not allowed in local scope -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -let - macro foo(ex) - end -end -""") -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -function f() - macro foo() - end -end -""") - end diff --git a/test/macros_ir.jl b/test/macros_ir.jl index b461826..56e4efc 100644 --- a/test/macros_ir.jl +++ b/test/macros_ir.jl @@ -34,3 +34,53 @@ end 3 (return %₁) 7 (return %₁) +######################################## +# Error: Macro with kw args +macro mmm(a; b=2) +end +#--------------------- +LoweringError: +macro mmm(a; b=2) +# └───┘ ── macros cannot accept keyword arguments +end + +######################################## +# Error: Bad macro name +macro mmm[](ex) +end +#--------------------- +LoweringError: +macro mmm[](ex) +# └───┘ ── invalid macro name +end + +######################################## +# Error: Macros not allowed in local scope +let + macro foo(ex) + end +end +#--------------------- +LoweringError: +let +# ┌──────────── + macro foo(ex) + end +#─────┘ ── macro is only allowed in global scope +end + +######################################## +# Error: Macros not allowed in local scope +function f() + macro foo() + end +end +#--------------------- +LoweringError: +function f() +# ┌────────── + macro foo() + end +#─────┘ ── macro is only allowed in global scope +end + diff --git a/test/misc_ir.jl b/test/misc_ir.jl index 5b8de6f..949fb11 100644 --- a/test/misc_ir.jl +++ b/test/misc_ir.jl @@ -144,3 +144,42 @@ LoweringError: (; a=1, f()) # └─┘ ── Invalid named tuple element +######################################## +# Error: Modules not allowed in local scope +let + module C + end +end +#--------------------- +LoweringError: +let +# ┌─────── + module C + end +#─────┘ ── module is only allowed in global scope +end + +######################################## +# Error: Modules not allowed in local scope +function f() + module C + end +end +#--------------------- +LoweringError: +function f() +# ┌─────── + module C + end +#─────┘ ── module is only allowed in global scope +end + +######################################## +# Basic type assert +x::T +#--------------------- +1 TestMod.x +2 TestMod.T +3 (call core.typeassert %₁ %₂) +4 (return %₃) + diff --git a/test/modules.jl b/test/modules.jl index e9e7155..66595ee 100644 --- a/test/modules.jl +++ b/test/modules.jl @@ -31,18 +31,4 @@ begin end """)) == Module -# Modules not allowed in local scope -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -let - module C - end -end -""") -@test_throws LoweringError JuliaLowering.include_string(test_mod, """ -function f() - module C - end -end -""") - end diff --git a/test/quoting_ir.jl b/test/quoting_ir.jl new file mode 100644 index 0000000..7fa05c5 --- /dev/null +++ b/test/quoting_ir.jl @@ -0,0 +1,45 @@ +######################################## +# Simple interpolation +quote + $x + 1 +end +#--------------------- +1 TestMod.x +2 (call core.tuple %₁) +3 (call JuliaLowering.interpolate_ast (inert (block (call-i ($ x) + 1))) %₂) +4 (return %₃) + +######################################## +# Trivial interpolation +:($x) +#--------------------- +1 TestMod.x +2 (call core.tuple %₁) +3 (call JuliaLowering.interpolate_ast (inert ($ x)) %₂) +4 (return %₃) + +######################################## +# Double escape +quote + quote + $$x + 1 + end +end +#--------------------- +1 TestMod.x +2 (call core.tuple %₁) +3 (call JuliaLowering.interpolate_ast (inert (block (quote (block (call-i ($ ($ x)) + 1))))) %₂) +4 (return %₃) + +######################################## +# Error: Double escape +quote + $$x + 1 +end +#--------------------- +LoweringError: +quote + $$x + 1 +# └┘ ── `$` expression outside string or quote block +end + diff --git a/test/utils.jl b/test/utils.jl index 6d1d51d..84428e6 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -134,6 +134,9 @@ function format_ir_for_test(mod, input, expect_error=false) ex = parsestmt(SyntaxTree, input) try x = JuliaLowering.lower(mod, ex) + if expect_error + error("Expected a lowering error in test case") + end ir = strip(sprint(JuliaLowering.print_ir, x)) return replace(ir, string(mod)=>"TestMod") catch exc