mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge remote-tracking branch 'remote/misc-fixes' into refcount-tracking
This commit is contained in:
commit
a7545cd872
18 changed files with 974 additions and 2433 deletions
|
|
@ -5070,13 +5070,171 @@ pub fn canonicalizeExpr(
|
|||
const free_vars_span = self.scratch_free_vars.spanFrom(free_vars_start);
|
||||
return CanonicalizedExpr{ .idx = expr_idx, .free_vars = if (free_vars_span.len > 0) free_vars_span else null };
|
||||
},
|
||||
.suffix_single_question => |_| {
|
||||
const feature = try self.env.insertString("canonicalize suffix_single_question expression");
|
||||
const expr_idx = try self.env.pushMalformed(Expr.Idx, Diagnostic{ .not_implemented = .{
|
||||
.feature = feature,
|
||||
.region = Region.zero(),
|
||||
} });
|
||||
return CanonicalizedExpr{ .idx = expr_idx, .free_vars = null };
|
||||
.suffix_single_question => |unary| {
|
||||
// Desugar `expr?` into:
|
||||
// match expr {
|
||||
// Ok(#ok) => #ok,
|
||||
// Err(#err) => return Err(#err),
|
||||
// }
|
||||
const region = self.parse_ir.tokenizedRegionToRegion(unary.region);
|
||||
|
||||
const free_vars_start = self.scratch_free_vars.top();
|
||||
|
||||
// Canonicalize the inner expression (the expression before `?`)
|
||||
const can_cond = try self.canonicalizeExpr(unary.expr) orelse return null;
|
||||
|
||||
// Use pre-interned identifiers for the Ok/Err values and tag names
|
||||
const ok_val_ident = self.env.idents.question_ok;
|
||||
const err_val_ident = self.env.idents.question_err;
|
||||
const ok_tag_ident = self.env.idents.ok;
|
||||
const err_tag_ident = self.env.idents.err;
|
||||
|
||||
// Mark the start of scratch match branches
|
||||
const scratch_top = self.env.store.scratchMatchBranchTop();
|
||||
|
||||
// === Branch 1: Ok(#ok) => #ok ===
|
||||
{
|
||||
// Enter a new scope for this branch
|
||||
try self.scopeEnter(self.env.gpa, false);
|
||||
defer self.scopeExit(self.env.gpa) catch {};
|
||||
|
||||
// Create the assign pattern for the Ok value
|
||||
const ok_assign_pattern_idx = try self.env.addPattern(Pattern{
|
||||
.assign = .{ .ident = ok_val_ident },
|
||||
}, region);
|
||||
|
||||
// Introduce the pattern into scope
|
||||
_ = try self.scopeIntroduceInternal(self.env.gpa, .ident, ok_val_ident, ok_assign_pattern_idx, false, true);
|
||||
|
||||
// Create pattern span for Ok tag argument
|
||||
const ok_patterns_start = self.env.store.scratchPatternTop();
|
||||
try self.env.store.addScratchPattern(ok_assign_pattern_idx);
|
||||
const ok_args_span = try self.env.store.patternSpanFrom(ok_patterns_start);
|
||||
|
||||
// Create the Ok tag pattern: Ok(#ok)
|
||||
const ok_tag_pattern_idx = try self.env.addPattern(Pattern{
|
||||
.applied_tag = .{
|
||||
.name = ok_tag_ident,
|
||||
.args = ok_args_span,
|
||||
},
|
||||
}, region);
|
||||
|
||||
// Create branch pattern
|
||||
const branch_pat_scratch_top = self.env.store.scratchMatchBranchPatternTop();
|
||||
const ok_branch_pattern_idx = try self.env.addMatchBranchPattern(Expr.Match.BranchPattern{
|
||||
.pattern = ok_tag_pattern_idx,
|
||||
.degenerate = false,
|
||||
}, region);
|
||||
try self.env.store.addScratchMatchBranchPattern(ok_branch_pattern_idx);
|
||||
const ok_branch_pat_span = try self.env.store.matchBranchPatternSpanFrom(branch_pat_scratch_top);
|
||||
|
||||
// Create the branch body: lookup #ok
|
||||
const ok_lookup_idx = try self.env.addExpr(CIR.Expr{ .e_lookup_local = .{
|
||||
.pattern_idx = ok_assign_pattern_idx,
|
||||
} }, region);
|
||||
// Mark the pattern as used
|
||||
try self.used_patterns.put(self.env.gpa, ok_assign_pattern_idx, {});
|
||||
|
||||
// Create the Ok branch
|
||||
const ok_branch_idx = try self.env.addMatchBranch(
|
||||
Expr.Match.Branch{
|
||||
.patterns = ok_branch_pat_span,
|
||||
.value = ok_lookup_idx,
|
||||
.guard = null,
|
||||
.redundant = @enumFromInt(0),
|
||||
},
|
||||
region,
|
||||
);
|
||||
try self.env.store.addScratchMatchBranch(ok_branch_idx);
|
||||
}
|
||||
|
||||
// === Branch 2: Err(#err) => return Err(#err) ===
|
||||
{
|
||||
// Enter a new scope for this branch
|
||||
try self.scopeEnter(self.env.gpa, false);
|
||||
defer self.scopeExit(self.env.gpa) catch {};
|
||||
|
||||
// Create the assign pattern for the Err value
|
||||
const err_assign_pattern_idx = try self.env.addPattern(Pattern{
|
||||
.assign = .{ .ident = err_val_ident },
|
||||
}, region);
|
||||
|
||||
// Introduce the pattern into scope
|
||||
_ = try self.scopeIntroduceInternal(self.env.gpa, .ident, err_val_ident, err_assign_pattern_idx, false, true);
|
||||
|
||||
// Create pattern span for Err tag argument
|
||||
const err_patterns_start = self.env.store.scratchPatternTop();
|
||||
try self.env.store.addScratchPattern(err_assign_pattern_idx);
|
||||
const err_args_span = try self.env.store.patternSpanFrom(err_patterns_start);
|
||||
|
||||
// Create the Err tag pattern: Err(#err)
|
||||
const err_tag_pattern_idx = try self.env.addPattern(Pattern{
|
||||
.applied_tag = .{
|
||||
.name = err_tag_ident,
|
||||
.args = err_args_span,
|
||||
},
|
||||
}, region);
|
||||
|
||||
// Create branch pattern
|
||||
const branch_pat_scratch_top = self.env.store.scratchMatchBranchPatternTop();
|
||||
const err_branch_pattern_idx = try self.env.addMatchBranchPattern(Expr.Match.BranchPattern{
|
||||
.pattern = err_tag_pattern_idx,
|
||||
.degenerate = false,
|
||||
}, region);
|
||||
try self.env.store.addScratchMatchBranchPattern(err_branch_pattern_idx);
|
||||
const err_branch_pat_span = try self.env.store.matchBranchPatternSpanFrom(branch_pat_scratch_top);
|
||||
|
||||
// Create the branch body: return Err(#err)
|
||||
// First, create lookup for #err
|
||||
const err_lookup_idx = try self.env.addExpr(CIR.Expr{ .e_lookup_local = .{
|
||||
.pattern_idx = err_assign_pattern_idx,
|
||||
} }, region);
|
||||
// Mark the pattern as used
|
||||
try self.used_patterns.put(self.env.gpa, err_assign_pattern_idx, {});
|
||||
|
||||
// Create Err(#err) tag expression
|
||||
const err_tag_args_start = self.env.store.scratchExprTop();
|
||||
try self.env.store.addScratchExpr(err_lookup_idx);
|
||||
const err_tag_args_span = try self.env.store.exprSpanFrom(err_tag_args_start);
|
||||
|
||||
const err_tag_expr_idx = try self.env.addExpr(CIR.Expr{
|
||||
.e_tag = .{
|
||||
.name = err_tag_ident,
|
||||
.args = err_tag_args_span,
|
||||
},
|
||||
}, region);
|
||||
|
||||
// Create return Err(#err) expression
|
||||
const return_expr_idx = try self.env.addExpr(CIR.Expr{ .e_return = .{
|
||||
.expr = err_tag_expr_idx,
|
||||
} }, region);
|
||||
|
||||
// Create the Err branch
|
||||
const err_branch_idx = try self.env.addMatchBranch(
|
||||
Expr.Match.Branch{
|
||||
.patterns = err_branch_pat_span,
|
||||
.value = return_expr_idx,
|
||||
.guard = null,
|
||||
.redundant = @enumFromInt(0),
|
||||
},
|
||||
region,
|
||||
);
|
||||
try self.env.store.addScratchMatchBranch(err_branch_idx);
|
||||
}
|
||||
|
||||
// Create span from scratch branches
|
||||
const branches_span = try self.env.store.matchBranchSpanFrom(scratch_top);
|
||||
|
||||
// Create the match expression
|
||||
const match_expr = Expr.Match{
|
||||
.cond = can_cond.idx,
|
||||
.branches = branches_span,
|
||||
.exhaustive = @enumFromInt(0), // Will be set during type checking
|
||||
};
|
||||
const expr_idx = try self.env.addExpr(CIR.Expr{ .e_match = match_expr }, region);
|
||||
|
||||
const free_vars_span = self.scratch_free_vars.spanFrom(free_vars_start);
|
||||
return CanonicalizedExpr{ .idx = expr_idx, .free_vars = if (free_vars_span.len > 0) free_vars_span else null };
|
||||
},
|
||||
.unary_op => |unary| {
|
||||
const region = self.parse_ir.tokenizedRegionToRegion(unary.region);
|
||||
|
|
@ -6348,21 +6506,63 @@ fn canonicalizePattern(
|
|||
.string => |e| {
|
||||
const region = self.parse_ir.tokenizedRegionToRegion(e.region);
|
||||
|
||||
// resolve to a string slice from the source
|
||||
const token_text = self.parse_ir.resolve(e.string_tok);
|
||||
// Get the string expression which contains the actual string parts
|
||||
const str_expr = self.parse_ir.store.getExpr(e.expr);
|
||||
|
||||
// TODO: Handle escape sequences
|
||||
// For now, just intern the raw string
|
||||
const literal = try self.env.insertString(token_text);
|
||||
switch (str_expr) {
|
||||
.string => |se| {
|
||||
// Get the parts of the string expression
|
||||
const parts = self.parse_ir.store.exprSlice(se.parts);
|
||||
|
||||
const str_pattern = Pattern{
|
||||
.str_literal = .{
|
||||
.literal = literal,
|
||||
// For simple string literals, there should be exactly one string_part
|
||||
if (parts.len == 1) {
|
||||
const part = self.parse_ir.store.getExpr(parts[0]);
|
||||
switch (part) {
|
||||
.string_part => |sp| {
|
||||
// Get the actual string content from the string_part token
|
||||
const part_text = self.parse_ir.resolve(sp.token);
|
||||
|
||||
// Process escape sequences
|
||||
const processed_text = try processEscapeSequences(self.env.gpa, part_text);
|
||||
defer if (processed_text.ptr != part_text.ptr) {
|
||||
self.env.gpa.free(processed_text);
|
||||
};
|
||||
|
||||
const literal = try self.env.insertString(processed_text);
|
||||
|
||||
const str_pattern = Pattern{
|
||||
.str_literal = .{
|
||||
.literal = literal,
|
||||
},
|
||||
};
|
||||
const pattern_idx = try self.env.addPattern(str_pattern, region);
|
||||
|
||||
return pattern_idx;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// For string patterns with interpolation or multiple parts,
|
||||
// we need more complex handling (not yet supported)
|
||||
const malformed = try self.env.pushMalformed(Pattern.Idx, Diagnostic{
|
||||
.not_implemented = .{
|
||||
.feature = try self.env.insertString("string patterns with interpolation"),
|
||||
.region = region,
|
||||
},
|
||||
});
|
||||
return malformed;
|
||||
},
|
||||
};
|
||||
const pattern_idx = try self.env.addPattern(str_pattern, region);
|
||||
|
||||
return pattern_idx;
|
||||
else => {
|
||||
// Unexpected expression type in string pattern
|
||||
const malformed = try self.env.pushMalformed(Pattern.Idx, Diagnostic{
|
||||
.pattern_arg_invalid = .{
|
||||
.region = region,
|
||||
},
|
||||
});
|
||||
return malformed;
|
||||
},
|
||||
}
|
||||
},
|
||||
.single_quote => |e| {
|
||||
return try self.canonicalizeSingleQuote(e.region, e.token, Pattern.Idx);
|
||||
|
|
|
|||
|
|
@ -163,6 +163,9 @@ pub const CommonIdents = extern struct {
|
|||
// from_utf8 error payload fields (BadUtf8 record)
|
||||
problem: Ident.Idx,
|
||||
index: Ident.Idx,
|
||||
// Synthetic identifiers for ? operator desugaring
|
||||
question_ok: Ident.Idx,
|
||||
question_err: Ident.Idx,
|
||||
|
||||
/// Insert all well-known identifiers into a CommonEnv.
|
||||
/// Use this when creating a fresh ModuleEnv from scratch.
|
||||
|
|
@ -228,6 +231,9 @@ pub const CommonIdents = extern struct {
|
|||
// from_utf8 error payload fields (BadUtf8 record)
|
||||
.problem = try common.insertIdent(gpa, Ident.for_text("problem")),
|
||||
.index = try common.insertIdent(gpa, Ident.for_text("index")),
|
||||
// Synthetic identifiers for ? operator desugaring
|
||||
.question_ok = try common.insertIdent(gpa, Ident.for_text("#ok")),
|
||||
.question_err = try common.insertIdent(gpa, Ident.for_text("#err")),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -296,6 +302,9 @@ pub const CommonIdents = extern struct {
|
|||
// from_utf8 error payload fields (BadUtf8 record)
|
||||
.problem = common.findIdent("problem") orelse unreachable,
|
||||
.index = common.findIdent("index") orelse unreachable,
|
||||
// Synthetic identifiers for ? operator desugaring
|
||||
.question_ok = common.findIdent("#ok") orelse unreachable,
|
||||
.question_err = common.findIdent("#err") orelse unreachable,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -681,3 +681,67 @@ test "fx platform run from different cwd" {
|
|||
// Verify stdout contains expected messages
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stdout, "Hello from stdout!") != null);
|
||||
}
|
||||
|
||||
test "question mark operator" {
|
||||
// Tests the `?` operator for error propagation.
|
||||
const allocator = testing.allocator;
|
||||
|
||||
try ensureRocBinary(allocator);
|
||||
|
||||
const run_result = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{
|
||||
"./zig-out/bin/roc",
|
||||
"test/fx/question_mark_operator.roc",
|
||||
},
|
||||
});
|
||||
defer allocator.free(run_result.stdout);
|
||||
defer allocator.free(run_result.stderr);
|
||||
|
||||
// The ? operator should unwrap Ok values and return "hello"
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stdout, "hello") != null);
|
||||
}
|
||||
|
||||
test "numeric fold" {
|
||||
// Tests List.fold with numeric accumulators.
|
||||
const allocator = testing.allocator;
|
||||
|
||||
try ensureRocBinary(allocator);
|
||||
|
||||
const run_result = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{
|
||||
"./zig-out/bin/roc",
|
||||
"test/fx/numeric_fold.roc",
|
||||
},
|
||||
});
|
||||
defer allocator.free(run_result.stdout);
|
||||
defer allocator.free(run_result.stderr);
|
||||
|
||||
// Verify we get the correct sum: 1+2+3+4+5 = 15
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stdout, "Sum: 15") != null);
|
||||
}
|
||||
|
||||
test "string literal pattern matching" {
|
||||
// Tests pattern matching on string literals in match expressions.
|
||||
const allocator = testing.allocator;
|
||||
|
||||
try ensureRocBinary(allocator);
|
||||
|
||||
const run_result = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{
|
||||
"./zig-out/bin/roc",
|
||||
"test/fx/string_pattern_matching.roc",
|
||||
},
|
||||
});
|
||||
defer allocator.free(run_result.stdout);
|
||||
defer allocator.free(run_result.stderr);
|
||||
|
||||
// Verify string patterns match correctly
|
||||
const has_alice = std.mem.indexOf(u8, run_result.stdout, "Hello Alice!") != null;
|
||||
const has_bob = std.mem.indexOf(u8, run_result.stdout, "Hey Bob!") != null;
|
||||
|
||||
try testing.expect(has_alice);
|
||||
try testing.expect(has_bob);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2034,9 +2034,17 @@ pub const Interpreter = struct {
|
|||
std.debug.assert(args.len == 2); // low-level .num_is_eq expects 2 arguments
|
||||
const lhs = try self.extractNumericValue(args[0]);
|
||||
const rhs = try self.extractNumericValue(args[1]);
|
||||
const result = switch (lhs) {
|
||||
.int => |l| l == rhs.int,
|
||||
.dec => |l| l.num == rhs.dec.num,
|
||||
const result: bool = switch (lhs) {
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| l == r,
|
||||
.dec => |r| l == @divTrunc(r.num, RocDec.one_point_zero_i128),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| l.num == r.num,
|
||||
.int => |r| l.num == @as(i128, r) * RocDec.one_point_zero_i128,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32, .f64 => {
|
||||
self.triggerCrash("Equality comparison not supported for F32/F64 due to floating point imprecision", false, roc_ops);
|
||||
return error.Crash;
|
||||
|
|
@ -2049,11 +2057,27 @@ pub const Interpreter = struct {
|
|||
std.debug.assert(args.len == 2); // low-level .num_is_gt expects 2 arguments
|
||||
const lhs = try self.extractNumericValue(args[0]);
|
||||
const rhs = try self.extractNumericValue(args[1]);
|
||||
const result = switch (lhs) {
|
||||
.int => |l| l > rhs.int,
|
||||
.f32 => |l| l > rhs.f32,
|
||||
.f64 => |l| l > rhs.f64,
|
||||
.dec => |l| l.num > rhs.dec.num,
|
||||
const result: bool = switch (lhs) {
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| l > r,
|
||||
// Int vs Dec: convert Dec to Int for comparison
|
||||
.dec => |r| l > @divTrunc(r.num, RocDec.one_point_zero_i128),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| l > r,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| l > r,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| l.num > r.num,
|
||||
// Dec vs Int: convert Int to Dec for comparison
|
||||
.int => |r| l.num > @as(i128, r) * RocDec.one_point_zero_i128,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
};
|
||||
return try self.makeBoolValue(result);
|
||||
},
|
||||
|
|
@ -2062,11 +2086,25 @@ pub const Interpreter = struct {
|
|||
std.debug.assert(args.len == 2); // low-level .num_is_gte expects 2 arguments
|
||||
const lhs = try self.extractNumericValue(args[0]);
|
||||
const rhs = try self.extractNumericValue(args[1]);
|
||||
const result = switch (lhs) {
|
||||
.int => |l| l >= rhs.int,
|
||||
.f32 => |l| l >= rhs.f32,
|
||||
.f64 => |l| l >= rhs.f64,
|
||||
.dec => |l| l.num >= rhs.dec.num,
|
||||
const result: bool = switch (lhs) {
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| l >= r,
|
||||
.dec => |r| l >= @divTrunc(r.num, RocDec.one_point_zero_i128),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| l >= r,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| l >= r,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| l.num >= r.num,
|
||||
.int => |r| l.num >= @as(i128, r) * RocDec.one_point_zero_i128,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
};
|
||||
return try self.makeBoolValue(result);
|
||||
},
|
||||
|
|
@ -2075,11 +2113,25 @@ pub const Interpreter = struct {
|
|||
std.debug.assert(args.len == 2); // low-level .num_is_lt expects 2 arguments
|
||||
const lhs = try self.extractNumericValue(args[0]);
|
||||
const rhs = try self.extractNumericValue(args[1]);
|
||||
const result = switch (lhs) {
|
||||
.int => |l| l < rhs.int,
|
||||
.f32 => |l| l < rhs.f32,
|
||||
.f64 => |l| l < rhs.f64,
|
||||
.dec => |l| l.num < rhs.dec.num,
|
||||
const result: bool = switch (lhs) {
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| l < r,
|
||||
.dec => |r| l < @divTrunc(r.num, RocDec.one_point_zero_i128),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| l < r,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| l < r,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| l.num < r.num,
|
||||
.int => |r| l.num < @as(i128, r) * RocDec.one_point_zero_i128,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
};
|
||||
return try self.makeBoolValue(result);
|
||||
},
|
||||
|
|
@ -2088,11 +2140,25 @@ pub const Interpreter = struct {
|
|||
std.debug.assert(args.len == 2); // low-level .num_is_lte expects 2 arguments
|
||||
const lhs = try self.extractNumericValue(args[0]);
|
||||
const rhs = try self.extractNumericValue(args[1]);
|
||||
const result = switch (lhs) {
|
||||
.int => |l| l <= rhs.int,
|
||||
.f32 => |l| l <= rhs.f32,
|
||||
.f64 => |l| l <= rhs.f64,
|
||||
.dec => |l| l.num <= rhs.dec.num,
|
||||
const result: bool = switch (lhs) {
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| l <= r,
|
||||
.dec => |r| l <= @divTrunc(r.num, RocDec.one_point_zero_i128),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| l <= r,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| l <= r,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| l.num <= r.num,
|
||||
.int => |r| l.num <= @as(i128, r) * RocDec.one_point_zero_i128,
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
};
|
||||
return try self.makeBoolValue(result);
|
||||
},
|
||||
|
|
@ -2126,10 +2192,24 @@ pub const Interpreter = struct {
|
|||
out.is_initialized = false;
|
||||
|
||||
switch (lhs) {
|
||||
.int => |l| try out.setInt(l + rhs.int),
|
||||
.f32 => |l| out.setF32(l + rhs.f32),
|
||||
.f64 => |l| out.setF64(l + rhs.f64),
|
||||
.dec => |l| out.setDec(RocDec.add(l, rhs.dec, roc_ops)),
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| try out.setInt(l + r),
|
||||
.dec => |r| try out.setInt(l + @divTrunc(r.num, RocDec.one_point_zero_i128)),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| out.setF32(l + r),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| out.setF64(l + r),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| out.setDec(RocDec.add(l, r, roc_ops)),
|
||||
.int => |r| out.setDec(RocDec.add(l, RocDec{ .num = @as(i128, r) * RocDec.one_point_zero_i128 }, roc_ops)),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
}
|
||||
out.is_initialized = true;
|
||||
return out;
|
||||
|
|
@ -2144,10 +2224,24 @@ pub const Interpreter = struct {
|
|||
out.is_initialized = false;
|
||||
|
||||
switch (lhs) {
|
||||
.int => |l| try out.setInt(l - rhs.int),
|
||||
.f32 => |l| out.setF32(l - rhs.f32),
|
||||
.f64 => |l| out.setF64(l - rhs.f64),
|
||||
.dec => |l| out.setDec(RocDec.sub(l, rhs.dec, roc_ops)),
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| try out.setInt(l - r),
|
||||
.dec => |r| try out.setInt(l - @divTrunc(r.num, RocDec.one_point_zero_i128)),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| out.setF32(l - r),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| out.setF64(l - r),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| out.setDec(RocDec.sub(l, r, roc_ops)),
|
||||
.int => |r| out.setDec(RocDec.sub(l, RocDec{ .num = @as(i128, r) * RocDec.one_point_zero_i128 }, roc_ops)),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
}
|
||||
out.is_initialized = true;
|
||||
return out;
|
||||
|
|
@ -2162,10 +2256,24 @@ pub const Interpreter = struct {
|
|||
out.is_initialized = false;
|
||||
|
||||
switch (lhs) {
|
||||
.int => |l| try out.setInt(l * rhs.int),
|
||||
.f32 => |l| out.setF32(l * rhs.f32),
|
||||
.f64 => |l| out.setF64(l * rhs.f64),
|
||||
.dec => |l| out.setDec(RocDec.mul(l, rhs.dec, roc_ops)),
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| try out.setInt(l * r),
|
||||
.dec => |r| try out.setInt(l * @divTrunc(r.num, RocDec.one_point_zero_i128)),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| out.setF32(l * r),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| out.setF64(l * r),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| out.setDec(RocDec.mul(l, r, roc_ops)),
|
||||
.int => |r| out.setDec(RocDec.mul(l, RocDec{ .num = @as(i128, r) * RocDec.one_point_zero_i128 }, roc_ops)),
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
}
|
||||
out.is_initialized = true;
|
||||
return out;
|
||||
|
|
@ -2180,21 +2288,43 @@ pub const Interpreter = struct {
|
|||
out.is_initialized = false;
|
||||
|
||||
switch (lhs) {
|
||||
.int => |l| {
|
||||
if (rhs.int == 0) return error.DivisionByZero;
|
||||
try out.setInt(@divTrunc(l, rhs.int));
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
try out.setInt(@divTrunc(l, r));
|
||||
},
|
||||
.dec => |r| {
|
||||
const r_int = @divTrunc(r.num, RocDec.one_point_zero_i128);
|
||||
if (r_int == 0) return error.DivisionByZero;
|
||||
try out.setInt(@divTrunc(l, r_int));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| {
|
||||
if (rhs.f32 == 0) return error.DivisionByZero;
|
||||
out.setF32(l / rhs.f32);
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
out.setF32(l / r);
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| {
|
||||
if (rhs.f64 == 0) return error.DivisionByZero;
|
||||
out.setF64(l / rhs.f64);
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
out.setF64(l / r);
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| {
|
||||
if (rhs.dec.num == 0) return error.DivisionByZero;
|
||||
out.setDec(RocDec.div(l, rhs.dec, roc_ops));
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| {
|
||||
if (r.num == 0) return error.DivisionByZero;
|
||||
out.setDec(RocDec.div(l, r, roc_ops));
|
||||
},
|
||||
.int => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
const r_dec = RocDec{ .num = @as(i128, r) * RocDec.one_point_zero_i128 };
|
||||
out.setDec(RocDec.div(l, r_dec, roc_ops));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
}
|
||||
out.is_initialized = true;
|
||||
|
|
@ -2210,22 +2340,44 @@ pub const Interpreter = struct {
|
|||
out.is_initialized = false;
|
||||
|
||||
switch (lhs) {
|
||||
.int => |l| {
|
||||
if (rhs.int == 0) return error.DivisionByZero;
|
||||
try out.setInt(@divTrunc(l, rhs.int));
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
try out.setInt(@divTrunc(l, r));
|
||||
},
|
||||
.dec => |r| {
|
||||
const r_int = @divTrunc(r.num, RocDec.one_point_zero_i128);
|
||||
if (r_int == 0) return error.DivisionByZero;
|
||||
try out.setInt(@divTrunc(l, r_int));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| {
|
||||
if (rhs.f32 == 0) return error.DivisionByZero;
|
||||
out.setF32(@trunc(l / rhs.f32));
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
out.setF32(@trunc(l / r));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| {
|
||||
if (rhs.f64 == 0) return error.DivisionByZero;
|
||||
out.setF64(@trunc(l / rhs.f64));
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
out.setF64(@trunc(l / r));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| {
|
||||
// For Dec, div and div_trunc are the same since it's already integer-like
|
||||
if (rhs.dec.num == 0) return error.DivisionByZero;
|
||||
out.setDec(RocDec.div(l, rhs.dec, roc_ops));
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| {
|
||||
// For Dec, div and div_trunc are the same since it's already integer-like
|
||||
if (r.num == 0) return error.DivisionByZero;
|
||||
out.setDec(RocDec.div(l, r, roc_ops));
|
||||
},
|
||||
.int => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
const r_dec = RocDec{ .num = @as(i128, r) * RocDec.one_point_zero_i128 };
|
||||
out.setDec(RocDec.div(l, r_dec, roc_ops));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
}
|
||||
out.is_initialized = true;
|
||||
|
|
@ -2241,21 +2393,43 @@ pub const Interpreter = struct {
|
|||
out.is_initialized = false;
|
||||
|
||||
switch (lhs) {
|
||||
.int => |l| {
|
||||
if (rhs.int == 0) return error.DivisionByZero;
|
||||
try out.setInt(@rem(l, rhs.int));
|
||||
.int => |l| switch (rhs) {
|
||||
.int => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
try out.setInt(@rem(l, r));
|
||||
},
|
||||
.dec => |r| {
|
||||
const r_int = @divTrunc(r.num, RocDec.one_point_zero_i128);
|
||||
if (r_int == 0) return error.DivisionByZero;
|
||||
try out.setInt(@rem(l, r_int));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f32 => |l| {
|
||||
if (rhs.f32 == 0) return error.DivisionByZero;
|
||||
out.setF32(@rem(l, rhs.f32));
|
||||
.f32 => |l| switch (rhs) {
|
||||
.f32 => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
out.setF32(@rem(l, r));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.f64 => |l| {
|
||||
if (rhs.f64 == 0) return error.DivisionByZero;
|
||||
out.setF64(@rem(l, rhs.f64));
|
||||
.f64 => |l| switch (rhs) {
|
||||
.f64 => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
out.setF64(@rem(l, r));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
.dec => |l| {
|
||||
if (rhs.dec.num == 0) return error.DivisionByZero;
|
||||
out.setDec(RocDec.rem(l, rhs.dec, roc_ops));
|
||||
.dec => |l| switch (rhs) {
|
||||
.dec => |r| {
|
||||
if (r.num == 0) return error.DivisionByZero;
|
||||
out.setDec(RocDec.rem(l, r, roc_ops));
|
||||
},
|
||||
.int => |r| {
|
||||
if (r == 0) return error.DivisionByZero;
|
||||
const r_dec = RocDec{ .num = @as(i128, r) * RocDec.one_point_zero_i128 };
|
||||
out.setDec(RocDec.rem(l, r_dec, roc_ops));
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
},
|
||||
}
|
||||
out.is_initialized = true;
|
||||
|
|
@ -4338,6 +4512,36 @@ pub const Interpreter = struct {
|
|||
if (lhs.layout.tag == .scalar and rhs.layout.tag == .scalar) {
|
||||
const lhs_scalar = lhs.layout.data.scalar;
|
||||
const rhs_scalar = rhs.layout.data.scalar;
|
||||
|
||||
// Handle numeric type mismatches (Int vs Dec)
|
||||
const lhs_is_numeric = lhs_scalar.tag == .int or lhs_scalar.tag == .frac;
|
||||
const rhs_is_numeric = rhs_scalar.tag == .int or rhs_scalar.tag == .frac;
|
||||
if (lhs_is_numeric and rhs_is_numeric) {
|
||||
// Allow comparing Int with Dec by converting
|
||||
const lhs_num = self.extractNumericValue(lhs) catch return error.TypeMismatch;
|
||||
const rhs_num = self.extractNumericValue(rhs) catch return error.TypeMismatch;
|
||||
return switch (lhs_num) {
|
||||
.int => |l| switch (rhs_num) {
|
||||
.int => |r| l == r,
|
||||
.dec => |r| l == @divTrunc(r.num, RocDec.one_point_zero_i128),
|
||||
else => false,
|
||||
},
|
||||
.dec => |l| switch (rhs_num) {
|
||||
.dec => |r| l.num == r.num,
|
||||
.int => |r| l.num == @as(i128, r) * RocDec.one_point_zero_i128,
|
||||
else => false,
|
||||
},
|
||||
.f32 => |l| switch (rhs_num) {
|
||||
.f32 => |r| l == r,
|
||||
else => false,
|
||||
},
|
||||
.f64 => |l| switch (rhs_num) {
|
||||
.f64 => |r| l == r,
|
||||
else => false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (lhs_scalar.tag != rhs_scalar.tag) return error.TypeMismatch;
|
||||
|
||||
switch (lhs_scalar.tag) {
|
||||
|
|
@ -5655,14 +5859,15 @@ pub const Interpreter = struct {
|
|||
try self.ensureVarLayoutCapacity(idx + 1);
|
||||
const slot_ptr = &self.var_to_layout_slot.items[idx];
|
||||
|
||||
// If we have a flex var, default it to Dec
|
||||
// This is the interpreter-time defaulting for numeric literals
|
||||
// If we have a flex var, default it to I64 (not Dec)
|
||||
// This is the interpreter-time defaulting for unresolved numeric types.
|
||||
// Integer literals should default to I64, not Dec.
|
||||
if (resolved.desc.content == .flex) {
|
||||
// Directly return Dec's scalar layout
|
||||
const dec_layout = layout.Layout.frac(types.Frac.Precision.dec);
|
||||
const dec_layout_idx = try self.runtime_layout_store.insertLayout(dec_layout);
|
||||
slot_ptr.* = @intFromEnum(dec_layout_idx) + 1;
|
||||
return dec_layout;
|
||||
// Default to I64 for better compatibility with integer operations
|
||||
const i64_layout = layout.Layout.int(types.Int.Precision.i64);
|
||||
const i64_layout_idx = try self.runtime_layout_store.insertLayout(i64_layout);
|
||||
slot_ptr.* = @intFromEnum(i64_layout_idx) + 1;
|
||||
return i64_layout;
|
||||
}
|
||||
if (slot_ptr.* != 0) {
|
||||
const layout_idx_plus_one = slot_ptr.*;
|
||||
|
|
@ -7242,18 +7447,87 @@ pub const Interpreter = struct {
|
|||
};
|
||||
|
||||
// Get LHS and RHS type info
|
||||
// Note: Both operands should be unified to the same type by the type checker
|
||||
const lhs_ct_var = can.ModuleEnv.varFrom(binop.lhs);
|
||||
var lhs_rt_var = try self.translateTypeVar(self.env, lhs_ct_var);
|
||||
const lhs_rt_var = try self.translateTypeVar(self.env, lhs_ct_var);
|
||||
const rhs_ct_var = can.ModuleEnv.varFrom(binop.rhs);
|
||||
const rhs_rt_var = try self.translateTypeVar(self.env, rhs_ct_var);
|
||||
|
||||
// Resolve the lhs type - if flex/rigid, default to Dec
|
||||
// Ensure both operands have the same numeric type.
|
||||
// Strategy:
|
||||
// - If one operand is concrete (not flex/rigid), unify the other with it
|
||||
// - If both are unresolved (flex/rigid), default both to Dec
|
||||
const lhs_resolved = self.runtime_types.resolveVar(lhs_rt_var);
|
||||
if (lhs_resolved.desc.content == .flex or lhs_resolved.desc.content == .rigid) {
|
||||
const rhs_resolved = self.runtime_types.resolveVar(rhs_rt_var);
|
||||
const lhs_is_flex = lhs_resolved.desc.content == .flex or lhs_resolved.desc.content == .rigid;
|
||||
const rhs_is_flex = rhs_resolved.desc.content == .flex or rhs_resolved.desc.content == .rigid;
|
||||
|
||||
if (lhs_is_flex and rhs_is_flex) {
|
||||
// Both unresolved - default both to Dec
|
||||
const dec_content = try self.mkNumberTypeContentRuntime("Dec");
|
||||
const dec_var = try self.runtime_types.freshFromContent(dec_content);
|
||||
lhs_rt_var = dec_var;
|
||||
_ = try unify.unify(
|
||||
self.env,
|
||||
self.runtime_types,
|
||||
&self.problems,
|
||||
&self.snapshots,
|
||||
&self.unify_scratch,
|
||||
&self.unify_scratch.occurs_scratch,
|
||||
unify.ModuleEnvLookup{
|
||||
.interpreter_lookup_ctx = @ptrCast(&self.module_envs),
|
||||
.interpreter_lookup_fn = interpreterLookupModuleEnv,
|
||||
},
|
||||
lhs_rt_var,
|
||||
dec_var,
|
||||
);
|
||||
_ = try unify.unify(
|
||||
self.env,
|
||||
self.runtime_types,
|
||||
&self.problems,
|
||||
&self.snapshots,
|
||||
&self.unify_scratch,
|
||||
&self.unify_scratch.occurs_scratch,
|
||||
unify.ModuleEnvLookup{
|
||||
.interpreter_lookup_ctx = @ptrCast(&self.module_envs),
|
||||
.interpreter_lookup_fn = interpreterLookupModuleEnv,
|
||||
},
|
||||
rhs_rt_var,
|
||||
dec_var,
|
||||
);
|
||||
} else if (lhs_is_flex and !rhs_is_flex) {
|
||||
// LHS is flex, RHS is concrete - unify LHS with RHS
|
||||
_ = try unify.unify(
|
||||
self.env,
|
||||
self.runtime_types,
|
||||
&self.problems,
|
||||
&self.snapshots,
|
||||
&self.unify_scratch,
|
||||
&self.unify_scratch.occurs_scratch,
|
||||
unify.ModuleEnvLookup{
|
||||
.interpreter_lookup_ctx = @ptrCast(&self.module_envs),
|
||||
.interpreter_lookup_fn = interpreterLookupModuleEnv,
|
||||
},
|
||||
lhs_rt_var,
|
||||
rhs_rt_var,
|
||||
);
|
||||
} else if (!lhs_is_flex and rhs_is_flex) {
|
||||
// RHS is flex, LHS is concrete - unify RHS with LHS
|
||||
_ = try unify.unify(
|
||||
self.env,
|
||||
self.runtime_types,
|
||||
&self.problems,
|
||||
&self.snapshots,
|
||||
&self.unify_scratch,
|
||||
&self.unify_scratch.occurs_scratch,
|
||||
unify.ModuleEnvLookup{
|
||||
.interpreter_lookup_ctx = @ptrCast(&self.module_envs),
|
||||
.interpreter_lookup_fn = interpreterLookupModuleEnv,
|
||||
},
|
||||
rhs_rt_var,
|
||||
lhs_rt_var,
|
||||
);
|
||||
}
|
||||
// If both are concrete, they should already match (type checker ensures this)
|
||||
|
||||
// For != we need to negate the result of is_eq
|
||||
const negate_result = binop.op == .ne;
|
||||
|
|
|
|||
10
test/fx/numeric_fold.roc
Normal file
10
test/fx/numeric_fold.roc
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
app [main!] { pf: platform "./platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
|
||||
# Tests List.fold with numeric accumulators.
|
||||
|
||||
main! = || {
|
||||
sum = [1, 2, 3, 4, 5].fold(0, |acc, n| acc + n)
|
||||
Stdout.line!("Sum: ${I64.to_str(sum)}")
|
||||
}
|
||||
19
test/fx/question_mark_operator.roc
Normal file
19
test/fx/question_mark_operator.roc
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
app [main!] { pf: platform "./platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
|
||||
# Tests the `?` operator for error propagation.
|
||||
# The operator unwraps Ok values or early-returns Err values.
|
||||
|
||||
get_greeting : {} -> Try(Str, [ListWasEmpty])
|
||||
get_greeting = |{}| {
|
||||
first = List.first(["hello"])?
|
||||
Ok(first)
|
||||
}
|
||||
|
||||
main! = || {
|
||||
match get_greeting({}) {
|
||||
Ok(greeting) => Stdout.line!(greeting)
|
||||
Err(ListWasEmpty) => Stdout.line!("List was empty!")
|
||||
}
|
||||
}
|
||||
19
test/fx/string_pattern_matching.roc
Normal file
19
test/fx/string_pattern_matching.roc
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
app [main!] { pf: platform "./platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
|
||||
# Tests pattern matching on string literals in match expressions.
|
||||
|
||||
main! = || {
|
||||
greet("Alice")
|
||||
greet("Bob")
|
||||
}
|
||||
|
||||
greet = |name| {
|
||||
message = match name {
|
||||
"Alice" => "Hello Alice!"
|
||||
"Bob" => "Hey Bob!"
|
||||
_ => "Hello stranger!"
|
||||
}
|
||||
Stdout.line!(message)
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
# META
|
||||
~~~ini
|
||||
description=fuzz crash
|
||||
type=file
|
||||
~~~
|
||||
# SOURCE
|
||||
~~~roc
|
||||
ff8.8.d
|
||||
~~~
|
||||
# EXPECTED
|
||||
PARSE ERROR - fuzz_crash_007.md:1:1:1:4
|
||||
PARSE ERROR - fuzz_crash_007.md:1:4:1:6
|
||||
PARSE ERROR - fuzz_crash_007.md:1:6:1:8
|
||||
MISSING MAIN! FUNCTION - fuzz_crash_007.md:1:1:1:8
|
||||
# PROBLEMS
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**fuzz_crash_007.md:1:1:1:4:**
|
||||
```roc
|
||||
ff8.8.d
|
||||
```
|
||||
^^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**fuzz_crash_007.md:1:4:1:6:**
|
||||
```roc
|
||||
ff8.8.d
|
||||
```
|
||||
^^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**fuzz_crash_007.md:1:6:1:8:**
|
||||
```roc
|
||||
ff8.8.d
|
||||
```
|
||||
^^
|
||||
|
||||
|
||||
**MISSING MAIN! FUNCTION**
|
||||
Default app modules must have a `main!` function.
|
||||
|
||||
No `main!` function was found.
|
||||
|
||||
Add a main! function like:
|
||||
`main! = |arg| { ... }`
|
||||
**fuzz_crash_007.md:1:1:1:8:**
|
||||
```roc
|
||||
ff8.8.d
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
LowerIdent,NoSpaceDotInt,NoSpaceDotLowerIdent,
|
||||
EndOfFile,
|
||||
~~~
|
||||
# PARSE
|
||||
~~~clojure
|
||||
(file
|
||||
(type-module)
|
||||
(statements
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))))
|
||||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
~~~
|
||||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir (empty true))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(inferred-types
|
||||
(defs)
|
||||
(expressions))
|
||||
~~~
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -750,10 +750,27 @@ This feature is not yet implemented: unsupported operator
|
|||
|
||||
This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!
|
||||
|
||||
**NOT IMPLEMENTED**
|
||||
This feature is not yet implemented: canonicalize suffix_single_question expression
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `e_fn` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**fuzz_crash_020.md:105:55:105:59:**
|
||||
```roc
|
||||
b?? 12 > 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 e_fn(arg1)?.od()?.ned()?.recd?
|
||||
```
|
||||
^^^^
|
||||
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `arg1` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**fuzz_crash_020.md:105:60:105:64:**
|
||||
```roc
|
||||
b?? 12 > 5 or 13 + 2 < 5 and 10 - 1 >= 16 or 12 <= 3 e_fn(arg1)?.od()?.ned()?.recd?
|
||||
```
|
||||
^^^^
|
||||
|
||||
This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `r` in this scope.
|
||||
|
|
@ -1681,7 +1698,7 @@ expect {
|
|||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-str (text """))))
|
||||
(p-str (text "for"))))
|
||||
(value
|
||||
(e-num (value "20"))))
|
||||
(branch
|
||||
|
|
@ -1914,7 +1931,30 @@ expect {
|
|||
(receiver
|
||||
(e-dot-access (field "unknown")
|
||||
(receiver
|
||||
(e-runtime-error (tag "not_implemented")))))))))
|
||||
(e-match
|
||||
(match
|
||||
(cond
|
||||
(e-call
|
||||
(e-runtime-error (tag "ident_not_in_scope"))
|
||||
(e-runtime-error (tag "ident_not_in_scope"))))
|
||||
(branches
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#ok")))))
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-return
|
||||
(e-tag (name "Err")
|
||||
(args
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#err"))))))))))))))))))
|
||||
(e-tag (name "Stdo!")
|
||||
(args
|
||||
(e-string
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,119 +0,0 @@
|
|||
# META
|
||||
~~~ini
|
||||
description=fuzz crash
|
||||
type=file
|
||||
~~~
|
||||
# SOURCE
|
||||
~~~roc
|
||||
app[]{f:platform""}{
|
||||
o:0}0
|
||||
~~~
|
||||
# EXPECTED
|
||||
PARSE ERROR - fuzz_crash_043.md:1:20:1:21
|
||||
UNEXPECTED TOKEN IN TYPE ANNOTATION - fuzz_crash_043.md:2:3:2:4
|
||||
PARSE ERROR - fuzz_crash_043.md:2:4:2:5
|
||||
PARSE ERROR - fuzz_crash_043.md:2:5:2:6
|
||||
MALFORMED TYPE - fuzz_crash_043.md:2:3:2:4
|
||||
# PROBLEMS
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**fuzz_crash_043.md:1:20:1:21:**
|
||||
```roc
|
||||
app[]{f:platform""}{
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**UNEXPECTED TOKEN IN TYPE ANNOTATION**
|
||||
The token **0** is not expected in a type annotation.
|
||||
Type annotations should contain types like _Str_, _Num a_, or _List U64_.
|
||||
|
||||
**fuzz_crash_043.md:2:3:2:4:**
|
||||
```roc
|
||||
o:0}0
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**fuzz_crash_043.md:2:4:2:5:**
|
||||
```roc
|
||||
o:0}0
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**PARSE ERROR**
|
||||
A parsing error occurred: `statement_unexpected_token`
|
||||
This is an unexpected parsing error. Please check your syntax.
|
||||
|
||||
**fuzz_crash_043.md:2:5:2:6:**
|
||||
```roc
|
||||
o:0}0
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**MALFORMED TYPE**
|
||||
This type annotation is malformed or contains invalid syntax.
|
||||
|
||||
**fuzz_crash_043.md:2:3:2:4:**
|
||||
```roc
|
||||
o:0}0
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
KwApp,OpenSquare,CloseSquare,OpenCurly,LowerIdent,OpColon,KwPlatform,StringStart,StringPart,StringEnd,CloseCurly,OpenCurly,
|
||||
LowerIdent,OpColon,Int,CloseCurly,Int,
|
||||
EndOfFile,
|
||||
~~~
|
||||
# PARSE
|
||||
~~~clojure
|
||||
(file
|
||||
(app
|
||||
(provides)
|
||||
(record-field (name "f")
|
||||
(e-string
|
||||
(e-string-part (raw ""))))
|
||||
(packages
|
||||
(record-field (name "f")
|
||||
(e-string
|
||||
(e-string-part (raw ""))))))
|
||||
(statements
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-type-anno (name "o")
|
||||
(ty-malformed (tag "ty_anno_unexpected_token")))
|
||||
(s-malformed (tag "statement_unexpected_token"))
|
||||
(s-malformed (tag "statement_unexpected_token"))))
|
||||
~~~
|
||||
# FORMATTED
|
||||
~~~roc
|
||||
app [] { f: platform "" }
|
||||
|
||||
o :
|
||||
~~~
|
||||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir
|
||||
(d-let
|
||||
(p-assign (ident "o"))
|
||||
(e-anno-only)
|
||||
(annotation
|
||||
(ty-malformed))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
(inferred-types
|
||||
(defs
|
||||
(patt (type "Error")))
|
||||
(expressions
|
||||
(expr (type "Error"))))
|
||||
~~~
|
||||
Binary file not shown.
|
|
@ -142,9 +142,9 @@ NO CHANGE
|
|||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-str (text """)))
|
||||
(p-str (text "hello")))
|
||||
(pattern (degenerate false)
|
||||
(p-str (text """))))
|
||||
(p-str (text "world"))))
|
||||
(value
|
||||
(e-string
|
||||
(e-literal (string "greetings")))))
|
||||
|
|
|
|||
|
|
@ -11,12 +11,30 @@ some_fn(arg1)?
|
|||
.record_field?
|
||||
~~~
|
||||
# EXPECTED
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
UNDEFINED VARIABLE - record_access_multiline_formatting_1.md:1:1:1:8
|
||||
UNDEFINED VARIABLE - record_access_multiline_formatting_1.md:1:9:1:13
|
||||
# PROBLEMS
|
||||
**NOT IMPLEMENTED**
|
||||
This feature is not yet implemented: canonicalize suffix_single_question expression
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `some_fn` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**record_access_multiline_formatting_1.md:1:1:1:8:**
|
||||
```roc
|
||||
some_fn(arg1)?
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `arg1` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**record_access_multiline_formatting_1.md:1:9:1:13:**
|
||||
```roc
|
||||
some_fn(arg1)?
|
||||
```
|
||||
^^^^
|
||||
|
||||
This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
|
|
@ -56,7 +74,30 @@ NO CHANGE
|
|||
(receiver
|
||||
(e-dot-access (field "unknown")
|
||||
(receiver
|
||||
(e-runtime-error (tag "not_implemented"))))))))
|
||||
(e-match
|
||||
(match
|
||||
(cond
|
||||
(e-call
|
||||
(e-runtime-error (tag "ident_not_in_scope"))
|
||||
(e-runtime-error (tag "ident_not_in_scope"))))
|
||||
(branches
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#ok")))))
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-return
|
||||
(e-tag (name "Err")
|
||||
(args
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#err")))))))))))))))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -11,12 +11,30 @@ some_fn(arg1)? # Comment 1
|
|||
.record_field?
|
||||
~~~
|
||||
# EXPECTED
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
UNDEFINED VARIABLE - record_access_multiline_formatting_4.md:1:1:1:8
|
||||
UNDEFINED VARIABLE - record_access_multiline_formatting_4.md:1:9:1:13
|
||||
# PROBLEMS
|
||||
**NOT IMPLEMENTED**
|
||||
This feature is not yet implemented: canonicalize suffix_single_question expression
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `some_fn` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**record_access_multiline_formatting_4.md:1:1:1:8:**
|
||||
```roc
|
||||
some_fn(arg1)? # Comment 1
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `arg1` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**record_access_multiline_formatting_4.md:1:9:1:13:**
|
||||
```roc
|
||||
some_fn(arg1)? # Comment 1
|
||||
```
|
||||
^^^^
|
||||
|
||||
This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
|
|
@ -56,7 +74,30 @@ NO CHANGE
|
|||
(receiver
|
||||
(e-dot-access (field "unknown")
|
||||
(receiver
|
||||
(e-runtime-error (tag "not_implemented"))))))))
|
||||
(e-match
|
||||
(match
|
||||
(cond
|
||||
(e-call
|
||||
(e-runtime-error (tag "ident_not_in_scope"))
|
||||
(e-runtime-error (tag "ident_not_in_scope"))))
|
||||
(branches
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#ok")))))
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-return
|
||||
(e-tag (name "Err")
|
||||
(args
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#err")))))))))))))))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -8,12 +8,30 @@ type=expr
|
|||
some_fn(arg1)?.static_dispatch_method()?.next_static_dispatch_method()?.record_field?
|
||||
~~~
|
||||
# EXPECTED
|
||||
NOT IMPLEMENTED - :0:0:0:0
|
||||
UNDEFINED VARIABLE - static_dispatch_super_test.md:1:1:1:8
|
||||
UNDEFINED VARIABLE - static_dispatch_super_test.md:1:9:1:13
|
||||
# PROBLEMS
|
||||
**NOT IMPLEMENTED**
|
||||
This feature is not yet implemented: canonicalize suffix_single_question expression
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `some_fn` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**static_dispatch_super_test.md:1:1:1:8:**
|
||||
```roc
|
||||
some_fn(arg1)?.static_dispatch_method()?.next_static_dispatch_method()?.record_field?
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `arg1` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**static_dispatch_super_test.md:1:9:1:13:**
|
||||
```roc
|
||||
some_fn(arg1)?.static_dispatch_method()?.next_static_dispatch_method()?.record_field?
|
||||
```
|
||||
^^^^
|
||||
|
||||
This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
|
|
@ -50,7 +68,30 @@ NO CHANGE
|
|||
(receiver
|
||||
(e-dot-access (field "unknown")
|
||||
(receiver
|
||||
(e-runtime-error (tag "not_implemented"))))))))
|
||||
(e-match
|
||||
(match
|
||||
(cond
|
||||
(e-call
|
||||
(e-runtime-error (tag "ident_not_in_scope"))
|
||||
(e-runtime-error (tag "ident_not_in_scope"))))
|
||||
(branches
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#ok")))))
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-return
|
||||
(e-tag (name "Err")
|
||||
(args
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#err")))))))))))))))))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -697,15 +697,38 @@ This feature is not yet implemented: unsupported operator
|
|||
|
||||
This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!
|
||||
|
||||
**NOT IMPLEMENTED**
|
||||
This feature is not yet implemented: canonicalize suffix_single_question expression
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `some_fn` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!
|
||||
**syntax_grab_bag.md:189:26:189:33:**
|
||||
```roc
|
||||
static_dispatch_style = some_fn(arg1)?.static_dispatch_method()?.next_static_dispatch_method()?.record_field?
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
**NOT IMPLEMENTED**
|
||||
This feature is not yet implemented: canonicalize suffix_single_question expression
|
||||
|
||||
This error doesn't have a proper diagnostic report yet. Let us know if you want to help improve Roc's error messages!
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `arg1` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**syntax_grab_bag.md:189:34:189:38:**
|
||||
```roc
|
||||
static_dispatch_style = some_fn(arg1)?.static_dispatch_method()?.next_static_dispatch_method()?.record_field?
|
||||
```
|
||||
^^^^
|
||||
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `line!` in this scope.
|
||||
Is there an `import` or `exposing` missing up-top?
|
||||
|
||||
**syntax_grab_bag.md:190:2:190:14:**
|
||||
```roc
|
||||
Stdout.line!(interpolated)?
|
||||
```
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNDEFINED VARIABLE**
|
||||
Nothing is named `line!` in this scope.
|
||||
|
|
@ -740,18 +763,6 @@ The unused variable is declared here:
|
|||
^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `interpolated` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_interpolated` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**syntax_grab_bag.md:165:2:165:14:**
|
||||
```roc
|
||||
interpolated = "Hello, ${world}"
|
||||
```
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `record` is not used anywhere in your code.
|
||||
|
||||
|
|
@ -948,6 +959,17 @@ It has the type:
|
|||
But I expected it to be:
|
||||
_Str_
|
||||
|
||||
**UNUSED VALUE**
|
||||
This expression produces a value, but it's not being used:
|
||||
**syntax_grab_bag.md:190:2:190:29:**
|
||||
```roc
|
||||
Stdout.line!(interpolated)?
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It has the type:
|
||||
_d_
|
||||
|
||||
**TYPE MISMATCH**
|
||||
This expression is used in an unexpected way:
|
||||
**syntax_grab_bag.md:144:9:196:2:**
|
||||
|
|
@ -2043,15 +2065,15 @@ expect {
|
|||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-str (text """))))
|
||||
(p-str (text "foo"))))
|
||||
(value
|
||||
(e-num (value "100"))))
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-str (text """)))
|
||||
(p-str (text "foo")))
|
||||
(pattern (degenerate false)
|
||||
(p-str (text """))))
|
||||
(p-str (text "bar"))))
|
||||
(value
|
||||
(e-num (value "200"))))
|
||||
(branch
|
||||
|
|
@ -2390,9 +2412,56 @@ expect {
|
|||
(receiver
|
||||
(e-dot-access (field "unknown")
|
||||
(receiver
|
||||
(e-runtime-error (tag "not_implemented")))))))))
|
||||
(e-match
|
||||
(match
|
||||
(cond
|
||||
(e-call
|
||||
(e-runtime-error (tag "ident_not_in_scope"))
|
||||
(e-runtime-error (tag "ident_not_in_scope"))))
|
||||
(branches
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#ok")))))
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-return
|
||||
(e-tag (name "Err")
|
||||
(args
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#err"))))))))))))))))))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "not_implemented")))
|
||||
(e-match
|
||||
(match
|
||||
(cond
|
||||
(e-call
|
||||
(e-runtime-error (tag "ident_not_in_scope"))
|
||||
(e-lookup-local
|
||||
(p-assign (ident "interpolated")))))
|
||||
(branches
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#ok")))))
|
||||
(branch
|
||||
(patterns
|
||||
(pattern (degenerate false)
|
||||
(p-applied-tag)))
|
||||
(value
|
||||
(e-return
|
||||
(e-tag (name "Err")
|
||||
(args
|
||||
(e-lookup-local
|
||||
(p-assign (ident "#err"))))))))))))
|
||||
(e-call
|
||||
(e-runtime-error (tag "ident_not_in_scope"))
|
||||
(e-string
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue