mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge remote-tracking branch 'origin/main' into more-eq-fixes
This commit is contained in:
commit
fb53d8098f
17 changed files with 424 additions and 30 deletions
|
|
@ -3547,8 +3547,10 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, env: *Env, expected: Expected)
|
|||
try self.unifyWith(expr_var, .{ .flex = Flex.init() }, env);
|
||||
},
|
||||
.e_dbg => |dbg| {
|
||||
does_fx = try self.checkExpr(dbg.expr, env, expected) or does_fx;
|
||||
_ = try self.unify(expr_var, ModuleEnv.varFrom(dbg.expr), env);
|
||||
// dbg evaluates its inner expression but returns {} (like expect)
|
||||
_ = try self.checkExpr(dbg.expr, env, .no_expectation);
|
||||
does_fx = true;
|
||||
try self.unifyWith(expr_var, .{ .structure = .empty_record }, env);
|
||||
},
|
||||
.e_expect => |expect| {
|
||||
does_fx = try self.checkExpr(expect.body, env, expected) or does_fx;
|
||||
|
|
|
|||
|
|
@ -1219,13 +1219,16 @@ test "check type - crash" {
|
|||
);
|
||||
}
|
||||
|
||||
// debug //
|
||||
// dbg //
|
||||
|
||||
test "check type - debug" {
|
||||
test "check type - dbg" {
|
||||
// dbg returns {} (not the value it's debugging), so it can be used
|
||||
// as a statement/side-effect without affecting the block's return type
|
||||
const source =
|
||||
\\y : U64
|
||||
\\y = {
|
||||
\\ debug 2
|
||||
\\ dbg 2
|
||||
\\ 42
|
||||
\\}
|
||||
\\
|
||||
\\main = {
|
||||
|
|
|
|||
|
|
@ -340,6 +340,46 @@ test "fx platform match with wildcard" {
|
|||
}
|
||||
}
|
||||
|
||||
test "fx platform dbg missing return value" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
try ensureRocBinary(allocator);
|
||||
|
||||
// Run an app that uses dbg as the last expression in main!.
|
||||
// dbg is treated as a statement (side-effect only) when it's the final
|
||||
// expression in a block, so the block returns {} as expected by main!.
|
||||
const run_result = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{
|
||||
"./zig-out/bin/roc",
|
||||
"--no-cache",
|
||||
"test/fx/dbg_missing_return.roc",
|
||||
},
|
||||
});
|
||||
defer allocator.free(run_result.stdout);
|
||||
defer allocator.free(run_result.stderr);
|
||||
|
||||
switch (run_result.term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
std.debug.print("Run failed with exit code {}\n", .{code});
|
||||
std.debug.print("STDOUT: {s}\n", .{run_result.stdout});
|
||||
std.debug.print("STDERR: {s}\n", .{run_result.stderr});
|
||||
return error.RunFailed;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
std.debug.print("Run terminated abnormally: {}\n", .{run_result.term});
|
||||
std.debug.print("STDOUT: {s}\n", .{run_result.stdout});
|
||||
std.debug.print("STDERR: {s}\n", .{run_result.stderr});
|
||||
return error.RunFailed;
|
||||
},
|
||||
}
|
||||
|
||||
// Verify that the dbg output was printed
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stderr, "this should work now") != null);
|
||||
}
|
||||
|
||||
test "fx platform check unused state var reports correct errors" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
|
|
|
|||
|
|
@ -461,7 +461,7 @@ pub const Interpreter = struct {
|
|||
defer result_value.decref(&self.runtime_layout_store, roc_ops);
|
||||
|
||||
// Only copy result if the result type is compatible with ret_ptr
|
||||
if (try self.shouldCopyResult(result_value, ret_ptr)) {
|
||||
if (try self.shouldCopyResult(result_value, ret_ptr, roc_ops)) {
|
||||
try result_value.copyToPtr(&self.runtime_layout_store, ret_ptr, roc_ops);
|
||||
}
|
||||
return;
|
||||
|
|
@ -471,7 +471,7 @@ pub const Interpreter = struct {
|
|||
defer result.decref(&self.runtime_layout_store, roc_ops);
|
||||
|
||||
// Only copy result if the result type is compatible with ret_ptr
|
||||
if (try self.shouldCopyResult(result, ret_ptr)) {
|
||||
if (try self.shouldCopyResult(result, ret_ptr, roc_ops)) {
|
||||
try result.copyToPtr(&self.runtime_layout_store, ret_ptr, roc_ops);
|
||||
}
|
||||
}
|
||||
|
|
@ -479,7 +479,7 @@ pub const Interpreter = struct {
|
|||
/// Check if the result should be copied to ret_ptr based on the result's layout.
|
||||
/// Returns false for zero-sized types (nothing to copy).
|
||||
/// Validates that ret_ptr is properly aligned for the result type.
|
||||
fn shouldCopyResult(self: *Interpreter, result: StackValue, ret_ptr: *anyopaque) !bool {
|
||||
fn shouldCopyResult(self: *Interpreter, result: StackValue, ret_ptr: *anyopaque, _: *RocOps) !bool {
|
||||
const result_size = self.runtime_layout_store.layoutSize(result.layout);
|
||||
if (result_size == 0) {
|
||||
// Zero-sized types don't need copying
|
||||
|
|
@ -494,7 +494,6 @@ pub const Interpreter = struct {
|
|||
const required_alignment = result.layout.alignment(self.runtime_layout_store.targetUsize());
|
||||
const ret_addr = @intFromPtr(ret_ptr);
|
||||
if (ret_addr % required_alignment.toByteUnits() != 0) {
|
||||
// Type mismatch detected at runtime
|
||||
return error.TypeMismatch;
|
||||
}
|
||||
|
||||
|
|
@ -1766,13 +1765,19 @@ pub const Interpreter = struct {
|
|||
return error.Crash;
|
||||
},
|
||||
.e_dbg => |dbg_expr| {
|
||||
// Evaluate and print the inner expression
|
||||
const inner_ct_var = can.ModuleEnv.varFrom(dbg_expr.expr);
|
||||
const inner_rt_var = try self.translateTypeVar(self.env, inner_ct_var);
|
||||
const value = try self.evalExprMinimal(dbg_expr.expr, roc_ops, inner_rt_var);
|
||||
defer value.decref(&self.runtime_layout_store, roc_ops);
|
||||
const rendered = try self.renderValueRocWithType(value, inner_rt_var);
|
||||
defer self.allocator.free(rendered);
|
||||
roc_ops.dbg(rendered);
|
||||
return value;
|
||||
// dbg returns {} (empty record) - use same pattern as e_expect
|
||||
const ct_var = can.ModuleEnv.varFrom(expr_idx);
|
||||
const rt_var = try self.translateTypeVar(self.env, ct_var);
|
||||
const layout_val = try self.getRuntimeLayout(rt_var);
|
||||
return try self.pushRaw(layout_val, 0);
|
||||
},
|
||||
// no tag handling in minimal evaluator
|
||||
.e_lambda => |lam| {
|
||||
|
|
|
|||
|
|
@ -1111,7 +1111,7 @@ fn parseStmtByType(self: *Parser, statementType: StatementType) Error!AST.Statem
|
|||
} });
|
||||
return statement_idx;
|
||||
},
|
||||
.KwDbg, .KwDebug => {
|
||||
.KwDbg => {
|
||||
const start = self.pos;
|
||||
self.advance();
|
||||
const expr = try self.parseExpr();
|
||||
|
|
@ -2145,7 +2145,7 @@ pub fn parseExprWithBp(self: *Parser, min_bp: u8) Error!AST.Expr.Idx {
|
|||
.branches = branches,
|
||||
} });
|
||||
},
|
||||
.KwDbg, .KwDebug => {
|
||||
.KwDbg => {
|
||||
self.advance();
|
||||
const e = try self.parseExpr();
|
||||
expr = try self.store.addExpr(.{ .dbg = .{
|
||||
|
|
|
|||
|
|
@ -135,7 +135,6 @@ pub const Token = struct {
|
|||
KwAs,
|
||||
KwCrash,
|
||||
KwDbg,
|
||||
KwDebug,
|
||||
KwElse,
|
||||
KwExpect,
|
||||
KwExposes,
|
||||
|
|
@ -275,7 +274,6 @@ pub const Token = struct {
|
|||
.KwAs,
|
||||
.KwCrash,
|
||||
.KwDbg,
|
||||
.KwDebug,
|
||||
.KwElse,
|
||||
.KwExpect,
|
||||
.KwExposes,
|
||||
|
|
@ -369,7 +367,6 @@ pub const Token = struct {
|
|||
.{ "as", .KwAs },
|
||||
.{ "crash", .KwCrash },
|
||||
.{ "dbg", .KwDbg },
|
||||
.{ "debug", .KwDebug },
|
||||
.{ "else", .KwElse },
|
||||
.{ "expect", .KwExpect },
|
||||
.{ "exposes", .KwExposes },
|
||||
|
|
@ -2210,9 +2207,6 @@ fn rebuildBufferForTesting(buf: []const u8, tokens: *TokenizedBuffer, alloc: std
|
|||
.KwDbg => {
|
||||
try buf2.appendSlice("dbg");
|
||||
},
|
||||
.KwDebug => {
|
||||
try buf2.appendSlice("debug");
|
||||
},
|
||||
.KwElse => {
|
||||
try buf2.appendSlice("else");
|
||||
},
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub const CommonMisspellings = struct {
|
|||
.{ "case", "`case` is not a keyword in Roc. Use `match` for pattern matching." },
|
||||
.{ "switch", "`switch` is not a keyword in Roc. Use `match` for pattern matching." },
|
||||
.{ "when", "`when` is not a keyword in Roc. Use `match` for pattern matching." },
|
||||
.{ "debug", "`debug` is not a keyword in Roc. Use `dbg` for debug printing." },
|
||||
.{ "then", "`then` is not a keyword in Roc. You can put the first branch of an `if` immediately after the condition, e.g. `if (condition) then_branch else else_branch`" },
|
||||
.{ "elif", "Roc uses `else if` for chaining conditions, not `elif`." },
|
||||
.{ "elseif", "Roc uses `else if` (two words) for chaining conditions." },
|
||||
|
|
@ -107,7 +108,7 @@ test "identifier misspellings lookup" {
|
|||
const tip = CommonMisspellings.getIdentifierTip("case");
|
||||
try std.testing.expect(tip != null);
|
||||
try std.testing.expectEqualStrings(
|
||||
"`case` is not a keyword in Roc. Use `when` for pattern matching.",
|
||||
"`case` is not a keyword in Roc. Use `match` for pattern matching.",
|
||||
tip.?,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
7
test/fx/dbg_missing_return.roc
Normal file
7
test/fx/dbg_missing_return.roc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
app [main!] { pf: platform "./platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
|
||||
main! = || {
|
||||
dbg "this should work now"
|
||||
}
|
||||
|
|
@ -433,7 +433,8 @@ fn setupWasm(gpa: std.mem.Allocator, arena: std.mem.Allocator, wasm_path: []cons
|
|||
// Create and instantiate the module instance using the gpa allocator for the VM
|
||||
var module_instance = try bytebox.createModuleInstance(.Stack, module_def, gpa);
|
||||
errdefer module_instance.destroy();
|
||||
try module_instance.instantiate(.{});
|
||||
// Use a larger stack size (256 KB instead of default 128 KB) to accommodate complex interpreter code
|
||||
try module_instance.instantiate(.{ .stack_size = 1024 * 256 });
|
||||
|
||||
logDebug("[INFO] WASM module instantiated successfully.\n", .{});
|
||||
|
||||
|
|
|
|||
|
|
@ -281,6 +281,7 @@ UNUSED VALUE - fuzz_crash_023.md:1:1:1:1
|
|||
TYPE MISMATCH - fuzz_crash_023.md:155:2:157:3
|
||||
UNUSED VALUE - fuzz_crash_023.md:155:2:157:3
|
||||
UNUSED VALUE - fuzz_crash_023.md:178:42:178:45
|
||||
TYPE MISMATCH - fuzz_crash_023.md:144:9:196:2
|
||||
# PROBLEMS
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `expected_expr_record_field_name`
|
||||
|
|
@ -1046,6 +1047,71 @@ This expression produces a value, but it's not being used:
|
|||
It has the type:
|
||||
_[Blue]_others_
|
||||
|
||||
**TYPE MISMATCH**
|
||||
This expression is used in an unexpected way:
|
||||
**fuzz_crash_023.md:144:9:196:2:**
|
||||
```roc
|
||||
main! = |_| { # Yeah I can leave a comment here
|
||||
world = "World"
|
||||
var number = 123
|
||||
expect blah == 1
|
||||
tag = Blue
|
||||
return # Comment after return keyword
|
||||
tag # Comment after return statement
|
||||
|
||||
# Just a random comment!
|
||||
|
||||
...
|
||||
match_time(
|
||||
..., # Single args with comment
|
||||
)
|
||||
some_func(
|
||||
dbg # After debug
|
||||
42, # After debug expr
|
||||
)
|
||||
crash # Comment after crash keyword
|
||||
"Unreachable!" # Comment after crash statement
|
||||
tag_with_payload = Ok(number)
|
||||
interpolated = "Hello, ${world}"
|
||||
list = [
|
||||
add_one(
|
||||
dbg # After dbg in list
|
||||
number, # after dbg expr as arg
|
||||
), # Comment one
|
||||
456, # Comment two
|
||||
789, # Comment three
|
||||
]
|
||||
for n in list {
|
||||
Stdout.line!("Adding ${n} to ${number}")
|
||||
number = number + n
|
||||
}
|
||||
record = { foo: 123, bar: "Hello", ;az: tag, qux: Ok(world), punned }
|
||||
tuple = (123, "World", tag, Ok(world), (nested, tuple), [1, 2, 3])
|
||||
multiline_tuple = (
|
||||
123,
|
||||
"World",
|
||||
tag1,
|
||||
Ok(world), # This one has a comment
|
||||
(nested, tuple),
|
||||
[1, 2, 3],
|
||||
)
|
||||
bin_op_result = Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5
|
||||
static_dispatch_style = some_fn(arg1)?.static_dispatch_method()?.next_static_dispatch_method()?.record_field?
|
||||
Stdout.line!(interpolated)?
|
||||
Stdout.line!(
|
||||
"How about ${ # Comment after string interpolation open
|
||||
Num.toStr(number) # Comment after string interpolation expr
|
||||
} as a string?",
|
||||
)
|
||||
} # Comment after top-level decl
|
||||
```
|
||||
|
||||
It has the type:
|
||||
_List(Error) => Error_
|
||||
|
||||
But the type annotation says it should have the type:
|
||||
_List(Error) -> Error_
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
KwApp,OpenSquare,LowerIdent,CloseSquare,OpenCurly,LowerIdent,OpColon,KwPlatform,StringStart,StringPart,StringEnd,CloseCurly,
|
||||
|
|
@ -2578,7 +2644,7 @@ expect {
|
|||
(patt (type "Error -> U64"))
|
||||
(patt (type "[Red][Blue, Green][ProvidedByCompiler], _arg -> Error"))
|
||||
(patt (type "Error"))
|
||||
(patt (type "List(Error) -> Error"))
|
||||
(patt (type "Error"))
|
||||
(patt (type "{}"))
|
||||
(patt (type "Error")))
|
||||
(type_decls
|
||||
|
|
@ -2625,7 +2691,7 @@ expect {
|
|||
(expr (type "Error -> U64"))
|
||||
(expr (type "[Red][Blue, Green][ProvidedByCompiler], _arg -> Error"))
|
||||
(expr (type "Error"))
|
||||
(expr (type "List(Error) -> Error"))
|
||||
(expr (type "Error"))
|
||||
(expr (type "{}"))
|
||||
(expr (type "Error"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ UNUSED VALUE - fuzz_crash_027.md:1:1:1:1
|
|||
TYPE MISMATCH - fuzz_crash_027.md:111:2:113:3
|
||||
UNUSED VALUE - fuzz_crash_027.md:111:2:113:3
|
||||
TYPE MISMATCH - fuzz_crash_027.md:143:2:147:3
|
||||
TYPE MISMATCH - fuzz_crash_027.md:100:9:148:2
|
||||
# PROBLEMS
|
||||
**LEADING ZERO**
|
||||
Numbers cannot have leading zeros.
|
||||
|
|
@ -972,6 +973,67 @@ It has the type:
|
|||
But the type annotation says it should have the type:
|
||||
_Try(d)_
|
||||
|
||||
**TYPE MISMATCH**
|
||||
This expression is used in an unexpected way:
|
||||
**fuzz_crash_027.md:100:9:148:2:**
|
||||
```roc
|
||||
main! = |_| { # Yeah Ie
|
||||
world = "World"
|
||||
var number = 123
|
||||
expect blah == 1
|
||||
tag = Blue
|
||||
return # Comd
|
||||
tag
|
||||
|
||||
# Jusnt!
|
||||
|
||||
...
|
||||
match_time(
|
||||
..., #
|
||||
)
|
||||
some_func(
|
||||
dbg # bug
|
||||
42, # Aft expr
|
||||
)
|
||||
crash "Unreachtement
|
||||
tag_with = Ok(number)
|
||||
ited = "Hello, ${world}"
|
||||
list = [
|
||||
add_one(
|
||||
dbg # Afin list
|
||||
e[, # afarg
|
||||
), 456, # ee
|
||||
]
|
||||
for n in list {
|
||||
line!("Adding ${n} to ${number}")
|
||||
number = number + n
|
||||
}
|
||||
record = { foo: 123, bar: "Hello", baz: tag, qux: Ok(world), punned }
|
||||
tuple = (123, "World", tag, Ok(world), (nested, tuple), [1, 2, 3])
|
||||
m_tuple = (
|
||||
123,
|
||||
"World",
|
||||
tag1,
|
||||
Ok(world), # Thisnt
|
||||
(nested, tuple),
|
||||
[1, 2, 3],
|
||||
)
|
||||
bsult = Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5
|
||||
stale = some_fn(arg1)?.statod()?.ned()?.recd?
|
||||
Stdoline!(
|
||||
"How about ${ #
|
||||
Num.toStr(number) # on expr
|
||||
} as a",
|
||||
)
|
||||
} # Commenl decl
|
||||
```
|
||||
|
||||
It has the type:
|
||||
_List(Error) => Error_
|
||||
|
||||
But the type annotation says it should have the type:
|
||||
_List(Error) -> Error_
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
KwApp,OpenSquare,LowerIdent,CloseSquare,OpenCurly,LowerIdent,OpColon,KwPlatform,StringStart,StringPart,StringEnd,CloseCurly,
|
||||
|
|
@ -2255,7 +2317,7 @@ expect {
|
|||
(patt (type "Bool -> d where [d.from_numeral : Numeral -> Try(d, [InvalidNumeral(Str)])]"))
|
||||
(patt (type "Error -> U64"))
|
||||
(patt (type "[Red, Blue][ProvidedByCompiler], _arg -> Error"))
|
||||
(patt (type "List(Error) -> Error"))
|
||||
(patt (type "Error"))
|
||||
(patt (type "{}"))
|
||||
(patt (type "Error")))
|
||||
(type_decls
|
||||
|
|
@ -2292,7 +2354,7 @@ expect {
|
|||
(expr (type "Bool -> d where [d.from_numeral : Numeral -> Try(d, [InvalidNumeral(Str)])]"))
|
||||
(expr (type "Error -> U64"))
|
||||
(expr (type "[Red, Blue][ProvidedByCompiler], _arg -> Error"))
|
||||
(expr (type "List(Error) -> Error"))
|
||||
(expr (type "Error"))
|
||||
(expr (type "{}"))
|
||||
(expr (type "Error"))))
|
||||
~~~
|
||||
|
|
|
|||
Binary file not shown.
84
test/snapshots/statement/dbg_as_arg.md
Normal file
84
test/snapshots/statement/dbg_as_arg.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# META
|
||||
~~~ini
|
||||
description=Debug as function argument
|
||||
type=snippet
|
||||
~~~
|
||||
# SOURCE
|
||||
~~~roc
|
||||
foo = |f| f(dbg 42)
|
||||
bar = |f| f(dbg(42))
|
||||
~~~
|
||||
# EXPECTED
|
||||
NIL
|
||||
# PROBLEMS
|
||||
NIL
|
||||
# TOKENS
|
||||
~~~zig
|
||||
LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,LowerIdent,NoSpaceOpenRound,KwDbg,Int,CloseRound,
|
||||
LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,LowerIdent,NoSpaceOpenRound,KwDbg,NoSpaceOpenRound,Int,CloseRound,CloseRound,
|
||||
EndOfFile,
|
||||
~~~
|
||||
# PARSE
|
||||
~~~clojure
|
||||
(file
|
||||
(type-module)
|
||||
(statements
|
||||
(s-decl
|
||||
(p-ident (raw "foo"))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-ident (raw "f")))
|
||||
(e-apply
|
||||
(e-ident (raw "f"))
|
||||
(e-dbg
|
||||
(e-int (raw "42"))))))
|
||||
(s-decl
|
||||
(p-ident (raw "bar"))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-ident (raw "f")))
|
||||
(e-apply
|
||||
(e-ident (raw "f"))
|
||||
(e-dbg
|
||||
(e-tuple
|
||||
(e-int (raw "42")))))))))
|
||||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
foo = |f| f(dbg 42)
|
||||
bar = |f| f(dbg (42))
|
||||
~~~
|
||||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir
|
||||
(d-let
|
||||
(p-assign (ident "foo"))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-assign (ident "f")))
|
||||
(e-call
|
||||
(e-lookup-local
|
||||
(p-assign (ident "f")))
|
||||
(e-dbg
|
||||
(e-num (value "42"))))))
|
||||
(d-let
|
||||
(p-assign (ident "bar"))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-assign (ident "f")))
|
||||
(e-call
|
||||
(e-lookup-local
|
||||
(p-assign (ident "f")))
|
||||
(e-dbg
|
||||
(e-num (value "42")))))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(inferred-types
|
||||
(defs
|
||||
(patt (type "({} -> a) => a"))
|
||||
(patt (type "({} -> a) => a")))
|
||||
(expressions
|
||||
(expr (type "({} -> a) => a"))
|
||||
(expr (type "({} -> a) => a"))))
|
||||
~~~
|
||||
63
test/snapshots/statement/dbg_last_in_block.md
Normal file
63
test/snapshots/statement/dbg_last_in_block.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# META
|
||||
~~~ini
|
||||
description=Debug as last expression in block should return {}
|
||||
type=snippet
|
||||
~~~
|
||||
# SOURCE
|
||||
~~~roc
|
||||
main = || {
|
||||
dbg "hello"
|
||||
}
|
||||
~~~
|
||||
# EXPECTED
|
||||
NIL
|
||||
# PROBLEMS
|
||||
NIL
|
||||
# TOKENS
|
||||
~~~zig
|
||||
LowerIdent,OpAssign,OpBar,OpBar,OpenCurly,
|
||||
KwDbg,StringStart,StringPart,StringEnd,
|
||||
CloseCurly,
|
||||
EndOfFile,
|
||||
~~~
|
||||
# PARSE
|
||||
~~~clojure
|
||||
(file
|
||||
(type-module)
|
||||
(statements
|
||||
(s-decl
|
||||
(p-ident (raw "main"))
|
||||
(e-lambda
|
||||
(args)
|
||||
(e-block
|
||||
(statements
|
||||
(s-dbg
|
||||
(e-string
|
||||
(e-string-part (raw "hello"))))))))))
|
||||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
main = || {
|
||||
dbg "hello"
|
||||
}
|
||||
~~~
|
||||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir
|
||||
(d-let
|
||||
(p-assign (ident "main"))
|
||||
(e-lambda
|
||||
(args)
|
||||
(e-block
|
||||
(e-dbg
|
||||
(e-string
|
||||
(e-literal (string "hello"))))))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(inferred-types
|
||||
(defs
|
||||
(patt (type "({}) => {}")))
|
||||
(expressions
|
||||
(expr (type "({}) => {}"))))
|
||||
~~~
|
||||
|
|
@ -62,7 +62,7 @@ test = {
|
|||
~~~clojure
|
||||
(inferred-types
|
||||
(defs
|
||||
(patt (type "a where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)])]")))
|
||||
(patt (type "{}")))
|
||||
(expressions
|
||||
(expr (type "a where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)])]"))))
|
||||
(expr (type "{}"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ foo = |num| {
|
|||
~~~clojure
|
||||
(inferred-types
|
||||
(defs
|
||||
(patt (type "a -> a where [a.to_str : a -> b]")))
|
||||
(patt (type "a => {} where [a.to_str : a -> _ret]")))
|
||||
(expressions
|
||||
(expr (type "a -> a where [a.to_str : a -> b]"))))
|
||||
(expr (type "a => {} where [a.to_str : a -> _ret]"))))
|
||||
~~~
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ INCOMPATIBLE MATCH PATTERNS - syntax_grab_bag.md:84:2:84:2
|
|||
UNUSED VALUE - syntax_grab_bag.md:1:1:1:1
|
||||
TYPE MISMATCH - syntax_grab_bag.md:155:2:157:3
|
||||
UNUSED VALUE - syntax_grab_bag.md:155:2:157:3
|
||||
TYPE MISMATCH - syntax_grab_bag.md:144:9:196:2
|
||||
# PROBLEMS
|
||||
**UNDECLARED TYPE**
|
||||
The type _Bar_ is not declared in this scope.
|
||||
|
|
@ -926,6 +927,71 @@ This expression produces a value, but it's not being used:
|
|||
It has the type:
|
||||
_d_
|
||||
|
||||
**TYPE MISMATCH**
|
||||
This expression is used in an unexpected way:
|
||||
**syntax_grab_bag.md:144:9:196:2:**
|
||||
```roc
|
||||
main! = |_| { # Yeah I can leave a comment here
|
||||
world = "World"
|
||||
var number = 123
|
||||
expect blah == 1
|
||||
tag = Blue
|
||||
return # Comment after return keyword
|
||||
tag # Comment after return statement
|
||||
|
||||
# Just a random comment!
|
||||
|
||||
...
|
||||
match_time(
|
||||
..., # Single args with comment
|
||||
)
|
||||
some_func(
|
||||
dbg # After debug
|
||||
42, # After debug expr
|
||||
)
|
||||
crash # Comment after crash keyword
|
||||
"Unreachable!" # Comment after crash statement
|
||||
tag_with_payload = Ok(number)
|
||||
interpolated = "Hello, ${world}"
|
||||
list = [
|
||||
add_one(
|
||||
dbg # After dbg in list
|
||||
number, # after dbg expr as arg
|
||||
), # Comment one
|
||||
456, # Comment two
|
||||
789, # Comment three
|
||||
]
|
||||
for n in list {
|
||||
Stdout.line!("Adding ${n} to ${number}")
|
||||
number = number + n
|
||||
}
|
||||
record = { foo: 123, bar: "Hello", baz: tag, qux: Ok(world), punned }
|
||||
tuple = (123, "World", tag, Ok(world), (nested, tuple), [1, 2, 3])
|
||||
multiline_tuple = (
|
||||
123,
|
||||
"World",
|
||||
tag1,
|
||||
Ok(world), # This one has a comment
|
||||
(nested, tuple),
|
||||
[1, 2, 3],
|
||||
)
|
||||
bin_op_result = Err(foo) ?? 12 > 5 * 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 / 5
|
||||
static_dispatch_style = some_fn(arg1)?.static_dispatch_method()?.next_static_dispatch_method()?.record_field?
|
||||
Stdout.line!(interpolated)?
|
||||
Stdout.line!(
|
||||
"How about ${ # Comment after string interpolation open
|
||||
Num.toStr(number) # Comment after string interpolation expr
|
||||
} as a string?",
|
||||
)
|
||||
} # Comment after top-level decl
|
||||
```
|
||||
|
||||
It has the type:
|
||||
_List(Error) => Error_
|
||||
|
||||
But the type annotation says it should have the type:
|
||||
_List(Error) -> Error_
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
KwApp,OpenSquare,LowerIdent,CloseSquare,OpenCurly,LowerIdent,OpColon,KwPlatform,StringStart,StringPart,StringEnd,CloseCurly,
|
||||
|
|
@ -2463,7 +2529,7 @@ expect {
|
|||
(patt (type "Bool -> d where [d.from_numeral : Numeral -> Try(d, [InvalidNumeral(Str)])]"))
|
||||
(patt (type "Error -> U64"))
|
||||
(patt (type "[Red][Blue, Green][ProvidedByCompiler], _arg -> Error"))
|
||||
(patt (type "List(Error) -> Error"))
|
||||
(patt (type "Error"))
|
||||
(patt (type "{}"))
|
||||
(patt (type "Error")))
|
||||
(type_decls
|
||||
|
|
@ -2509,7 +2575,7 @@ expect {
|
|||
(expr (type "Bool -> d where [d.from_numeral : Numeral -> Try(d, [InvalidNumeral(Str)])]"))
|
||||
(expr (type "Error -> U64"))
|
||||
(expr (type "[Red][Blue, Green][ProvidedByCompiler], _arg -> Error"))
|
||||
(expr (type "List(Error) -> Error"))
|
||||
(expr (type "Error"))
|
||||
(expr (type "{}"))
|
||||
(expr (type "Error"))))
|
||||
~~~
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue