mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge branch 'less-process-exit' of github.com:roc-lang/roc into less-process-exit
This commit is contained in:
commit
34d9bddec9
29 changed files with 563 additions and 180 deletions
|
|
@ -84,7 +84,7 @@ pub const Attributes = packed struct(u3) {
|
|||
return .{
|
||||
.effectful = std.mem.endsWith(u8, text, "!"),
|
||||
.ignored = std.mem.startsWith(u8, text, "_"),
|
||||
.reassignable = false,
|
||||
.reassignable = std.mem.startsWith(u8, text, "$"),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -319,6 +319,14 @@ test "from_bytes creates ignored identifier" {
|
|||
try std.testing.expect(result.attributes.reassignable == false);
|
||||
}
|
||||
|
||||
test "from_bytes creates reassignable identifier" {
|
||||
const result = try Ident.from_bytes("$reusable");
|
||||
try std.testing.expectEqualStrings("$reusable", result.raw_text);
|
||||
try std.testing.expect(result.attributes.effectful == false);
|
||||
try std.testing.expect(result.attributes.ignored == false);
|
||||
try std.testing.expect(result.attributes.reassignable == true);
|
||||
}
|
||||
|
||||
test "Ident.Store empty CompactWriter roundtrip" {
|
||||
const gpa = std.testing.allocator;
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ pub const CanonicalizedExpr = struct {
|
|||
const TypeVarProblemKind = enum {
|
||||
unused_type_var,
|
||||
type_var_marked_unused,
|
||||
type_var_ending_in_underscore,
|
||||
type_var_starting_with_dollar,
|
||||
};
|
||||
|
||||
const TypeVarProblem = struct {
|
||||
|
|
@ -5320,9 +5320,9 @@ fn scopeIntroduceVar(
|
|||
}
|
||||
|
||||
fn collectTypeVarProblems(ident: Ident.Idx, is_single_use: bool, ast_anno: AST.TypeAnno.Idx, scratch: *base.Scratch(TypeVarProblem)) std.mem.Allocator.Error!void {
|
||||
// Warn for type variables with trailing underscores
|
||||
// Warn for type variables starting with dollar sign (reusable markers)
|
||||
if (ident.attributes.reassignable) {
|
||||
try scratch.append(.{ .ident = ident, .problem = .type_var_ending_in_underscore, .ast_anno = ast_anno });
|
||||
try scratch.append(.{ .ident = ident, .problem = .type_var_starting_with_dollar, .ast_anno = ast_anno });
|
||||
}
|
||||
|
||||
// Should start with underscore but doesn't, or should not start with underscore but does.
|
||||
|
|
@ -5338,11 +5338,11 @@ fn reportTypeVarProblems(self: *Self, problems: []const TypeVarProblem) std.mem.
|
|||
const name_text = self.env.getIdent(problem.ident);
|
||||
|
||||
switch (problem.problem) {
|
||||
.type_var_ending_in_underscore => {
|
||||
const suggested_name_text = name_text[0 .. name_text.len - 1]; // Remove the trailing underscore
|
||||
.type_var_starting_with_dollar => {
|
||||
const suggested_name_text = name_text[1..]; // Remove the leading dollar sign
|
||||
const suggested_ident = self.env.insertIdent(base.Ident.for_text(suggested_name_text), Region.zero());
|
||||
|
||||
self.env.pushDiagnostic(Diagnostic{ .type_var_ending_in_underscore = .{
|
||||
self.env.pushDiagnostic(Diagnostic{ .type_var_starting_with_dollar = .{
|
||||
.name = problem.ident,
|
||||
.suggested_name = suggested_ident,
|
||||
.region = region,
|
||||
|
|
@ -5410,11 +5410,11 @@ fn processCollectedTypeVars(self: *Self) std.mem.Allocator.Error!void {
|
|||
const name_text = self.env.getIdent(problem.ident);
|
||||
|
||||
switch (problem.problem) {
|
||||
.type_var_ending_in_underscore => {
|
||||
const suggested_name_text = name_text[0 .. name_text.len - 1]; // Remove the trailing underscore
|
||||
.type_var_starting_with_dollar => {
|
||||
const suggested_name_text = name_text[1..]; // Remove the leading dollar sign
|
||||
const suggested_ident = self.env.insertIdent(base.Ident.for_text(suggested_name_text), Region.zero());
|
||||
|
||||
self.env.pushDiagnostic(Diagnostic{ .type_var_ending_in_underscore = .{
|
||||
self.env.pushDiagnostic(Diagnostic{ .type_var_starting_with_dollar = .{
|
||||
.name = problem.ident,
|
||||
.suggested_name = suggested_ident,
|
||||
.region = Region.zero(),
|
||||
|
|
@ -6718,35 +6718,91 @@ pub fn canonicalizeBlockStatement(self: *Self, ast_stmt: AST.Statement, ast_stmt
|
|||
},
|
||||
else => {
|
||||
// If the next stmt does not match this annotation,
|
||||
// then just add the annotation independently
|
||||
// create a Def with an e_anno_only body
|
||||
|
||||
// TODO: Capture diagnostic that this anno doesn't
|
||||
// have a corresponding def
|
||||
|
||||
const stmt_idx = try self.env.addStatement(Statement{
|
||||
.s_type_anno = .{
|
||||
.name = name_ident,
|
||||
.anno = type_anno_idx,
|
||||
.where = where_clauses,
|
||||
// Create the pattern for this def
|
||||
const pattern = Pattern{
|
||||
.assign = .{
|
||||
.ident = name_ident,
|
||||
},
|
||||
}, region);
|
||||
};
|
||||
const pattern_idx = try self.env.addPattern(pattern, region);
|
||||
|
||||
// Introduce the name to scope
|
||||
switch (try self.scopeIntroduceInternal(self.env.gpa, .ident, name_ident, pattern_idx, false, true)) {
|
||||
.success => {},
|
||||
.shadowing_warning => |shadowed_pattern_idx| {
|
||||
const original_region = self.env.store.getPatternRegion(shadowed_pattern_idx);
|
||||
try self.env.pushDiagnostic(Diagnostic{ .shadowing_warning = .{
|
||||
.ident = name_ident,
|
||||
.region = region,
|
||||
.original_region = original_region,
|
||||
} });
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
// Create the e_anno_only expression
|
||||
const anno_only_expr = try self.env.addExpr(Expr{ .e_anno_only = .{} }, region);
|
||||
|
||||
// Create the annotation structure
|
||||
const annotation = CIR.Annotation{
|
||||
.anno = type_anno_idx,
|
||||
.where = where_clauses,
|
||||
};
|
||||
const annotation_idx = try self.env.addAnnotation(annotation, region);
|
||||
|
||||
// Add the decl as a statement with the e_anno_only body
|
||||
const stmt_idx = try self.env.addStatement(Statement{ .s_decl = .{
|
||||
.pattern = pattern_idx,
|
||||
.expr = anno_only_expr,
|
||||
.anno = annotation_idx,
|
||||
} }, region);
|
||||
mb_canonicailzed_stmt = CanonicalizedStatement{ .idx = stmt_idx, .free_vars = null };
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// If the next stmt does not match this annotation,
|
||||
// then just add the annotation independently
|
||||
// create a Def with an e_anno_only body
|
||||
|
||||
// TODO: Capture diagnostic that this anno doesn't
|
||||
// have a corresponding def
|
||||
|
||||
const stmt_idx = try self.env.addStatement(Statement{
|
||||
.s_type_anno = .{
|
||||
.name = name_ident,
|
||||
.anno = type_anno_idx,
|
||||
.where = where_clauses,
|
||||
// Create the pattern for this def
|
||||
const pattern = Pattern{
|
||||
.assign = .{
|
||||
.ident = name_ident,
|
||||
},
|
||||
}, region);
|
||||
};
|
||||
const pattern_idx = try self.env.addPattern(pattern, region);
|
||||
|
||||
// Introduce the name to scope
|
||||
switch (try self.scopeIntroduceInternal(self.env.gpa, .ident, name_ident, pattern_idx, false, true)) {
|
||||
.success => {},
|
||||
.shadowing_warning => |shadowed_pattern_idx| {
|
||||
const original_region = self.env.store.getPatternRegion(shadowed_pattern_idx);
|
||||
try self.env.pushDiagnostic(Diagnostic{ .shadowing_warning = .{
|
||||
.ident = name_ident,
|
||||
.region = region,
|
||||
.original_region = original_region,
|
||||
} });
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
// Create the e_anno_only expression
|
||||
const anno_only_expr = try self.env.addExpr(Expr{ .e_anno_only = .{} }, region);
|
||||
|
||||
// Create the annotation structure
|
||||
const annotation = CIR.Annotation{
|
||||
.anno = type_anno_idx,
|
||||
.where = where_clauses,
|
||||
};
|
||||
const annotation_idx = try self.env.addAnnotation(annotation, region);
|
||||
|
||||
// Add the decl as a statement with the e_anno_only body
|
||||
const stmt_idx = try self.env.addStatement(Statement{ .s_decl = .{
|
||||
.pattern = pattern_idx,
|
||||
.expr = anno_only_expr,
|
||||
.anno = annotation_idx,
|
||||
} }, region);
|
||||
mb_canonicailzed_stmt = CanonicalizedStatement{ .idx = stmt_idx, .free_vars = null };
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ fn collectExprDependencies(
|
|||
},
|
||||
|
||||
// Literals have no dependencies
|
||||
.e_num, .e_frac_f32, .e_frac_f64, .e_dec, .e_dec_small, .e_str, .e_str_segment, .e_empty_list, .e_empty_record, .e_zero_argument_tag, .e_ellipsis => {},
|
||||
.e_num, .e_frac_f32, .e_frac_f64, .e_dec, .e_dec_small, .e_str, .e_str_segment, .e_empty_list, .e_empty_record, .e_zero_argument_tag, .e_ellipsis, .e_anno_only => {},
|
||||
|
||||
// External lookups reference other modules - skip for now
|
||||
.e_lookup_external => {},
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ pub const Diagnostic = union(enum) {
|
|||
suggested_name: Ident.Idx,
|
||||
region: Region,
|
||||
},
|
||||
type_var_ending_in_underscore: struct {
|
||||
type_var_starting_with_dollar: struct {
|
||||
name: Ident.Idx,
|
||||
suggested_name: Ident.Idx,
|
||||
region: Region,
|
||||
|
|
@ -302,7 +302,7 @@ pub const Diagnostic = union(enum) {
|
|||
.f64_pattern_literal => |d| d.region,
|
||||
.unused_type_var_name => |d| d.region,
|
||||
.type_var_marked_unused => |d| d.region,
|
||||
.type_var_ending_in_underscore => |d| d.region,
|
||||
.type_var_starting_with_dollar => |d| d.region,
|
||||
.underscore_in_type_declaration => |d| d.region,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -357,6 +357,14 @@ pub const Expr = union(enum) {
|
|||
/// launchTheNukes: |{}| ...
|
||||
/// ```
|
||||
e_ellipsis: struct {},
|
||||
/// A standalone type annotation without a body.
|
||||
/// This represents a type declaration that has no implementation.
|
||||
/// During type-checking, this expression is assigned the type from its annotation.
|
||||
///
|
||||
/// ```roc
|
||||
/// foo : {} -> {}
|
||||
/// ```
|
||||
e_anno_only: struct {},
|
||||
|
||||
pub const Idx = enum(u32) { _ };
|
||||
pub const Span = struct { span: DataSpan };
|
||||
|
|
@ -977,6 +985,14 @@ pub const Expr = union(enum) {
|
|||
const attrs = tree.beginNode();
|
||||
try tree.endNode(begin, attrs);
|
||||
},
|
||||
.e_anno_only => |_| {
|
||||
const begin = tree.beginNode();
|
||||
try tree.pushStaticAtom("e-anno-only");
|
||||
const region = ir.store.getExprRegion(expr_idx);
|
||||
try ir.appendRegionInfoToSExprTreeFromRegion(tree, region);
|
||||
const attrs = tree.beginNode();
|
||||
try tree.endNode(begin, attrs);
|
||||
},
|
||||
.e_crash => |e| {
|
||||
const begin = tree.beginNode();
|
||||
try tree.pushStaticAtom("e-crash");
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ pub const Tag = enum {
|
|||
expr_crash,
|
||||
expr_block,
|
||||
expr_ellipsis,
|
||||
expr_anno_only,
|
||||
expr_expect,
|
||||
expr_record_builder,
|
||||
match_branch,
|
||||
|
|
@ -201,7 +202,7 @@ pub const Tag = enum {
|
|||
diag_f64_pattern_literal,
|
||||
diag_unused_type_var_name,
|
||||
diag_type_var_marked_unused,
|
||||
diag_type_var_ending_in_underscore,
|
||||
diag_type_var_starting_with_dollar,
|
||||
diag_underscore_in_type_declaration,
|
||||
diagnostic_exposed_but_not_implemented,
|
||||
diag_redundant_exposed,
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ pub fn deinit(store: *NodeStore) void {
|
|||
/// Count of the diagnostic nodes in the ModuleEnv
|
||||
pub const MODULEENV_DIAGNOSTIC_NODE_COUNT = 57;
|
||||
/// Count of the expression nodes in the ModuleEnv
|
||||
pub const MODULEENV_EXPR_NODE_COUNT = 33;
|
||||
pub const MODULEENV_EXPR_NODE_COUNT = 34;
|
||||
/// Count of the statement nodes in the ModuleEnv
|
||||
pub const MODULEENV_STATEMENT_NODE_COUNT = 14;
|
||||
/// Count of the type annotation nodes in the ModuleEnv
|
||||
|
|
@ -628,6 +628,9 @@ pub fn getExpr(store: *const NodeStore, expr: CIR.Expr.Idx) CIR.Expr {
|
|||
.expr_ellipsis => {
|
||||
return CIR.Expr{ .e_ellipsis = .{} };
|
||||
},
|
||||
.expr_anno_only => {
|
||||
return CIR.Expr{ .e_anno_only = .{} };
|
||||
},
|
||||
.expr_expect => {
|
||||
return CIR.Expr{ .e_expect = .{
|
||||
.body = @enumFromInt(node.data_1),
|
||||
|
|
@ -1479,6 +1482,9 @@ pub fn addExpr(store: *NodeStore, expr: CIR.Expr, region: base.Region) Allocator
|
|||
.e_ellipsis => |_| {
|
||||
node.tag = .expr_ellipsis;
|
||||
},
|
||||
.e_anno_only => |_| {
|
||||
node.tag = .expr_anno_only;
|
||||
},
|
||||
.e_match => |e| {
|
||||
node.tag = .expr_match;
|
||||
|
||||
|
|
@ -2913,8 +2919,8 @@ pub fn addDiagnostic(store: *NodeStore, reason: CIR.Diagnostic) Allocator.Error!
|
|||
node.data_1 = @bitCast(r.name);
|
||||
node.data_2 = @bitCast(r.suggested_name);
|
||||
},
|
||||
.type_var_ending_in_underscore => |r| {
|
||||
node.tag = .diag_type_var_ending_in_underscore;
|
||||
.type_var_starting_with_dollar => |r| {
|
||||
node.tag = .diag_type_var_starting_with_dollar;
|
||||
region = r.region;
|
||||
node.data_1 = @bitCast(r.name);
|
||||
node.data_2 = @bitCast(r.suggested_name);
|
||||
|
|
@ -3228,7 +3234,7 @@ pub fn getDiagnostic(store: *const NodeStore, diagnostic: CIR.Diagnostic.Idx) CI
|
|||
.suggested_name = @bitCast(node.data_2),
|
||||
.region = store.getRegionAt(node_idx),
|
||||
} },
|
||||
.diag_type_var_ending_in_underscore => return CIR.Diagnostic{ .type_var_ending_in_underscore = .{
|
||||
.diag_type_var_starting_with_dollar => return CIR.Diagnostic{ .type_var_starting_with_dollar = .{
|
||||
.name = @bitCast(node.data_1),
|
||||
.suggested_name = @bitCast(node.data_2),
|
||||
.region = store.getRegionAt(node_idx),
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ test "compile tests" {
|
|||
std.testing.refAllDecls(@import("Statement.zig"));
|
||||
std.testing.refAllDecls(@import("TypeAnnotation.zig"));
|
||||
|
||||
std.testing.refAllDecls(@import("test/anno_only_test.zig"));
|
||||
std.testing.refAllDecls(@import("test/bool_test.zig"));
|
||||
std.testing.refAllDecls(@import("test/exposed_shadowing_test.zig"));
|
||||
std.testing.refAllDecls(@import("test/frac_test.zig"));
|
||||
|
|
|
|||
43
src/canonicalize/test/anno_only_test.zig
Normal file
43
src/canonicalize/test/anno_only_test.zig
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! Tests for standalone type annotation canonicalization.
|
||||
//!
|
||||
//! This module contains unit tests that verify the e_anno_only expression variant
|
||||
//! works correctly in the compiler's canonical internal representation (CIR).
|
||||
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const CIR = @import("../CIR.zig");
|
||||
|
||||
test "e_anno_only expression variant exists" {
|
||||
// Create an e_anno_only expression
|
||||
const expr = CIR.Expr{ .e_anno_only = .{} };
|
||||
|
||||
// Verify it's the correct variant
|
||||
switch (expr) {
|
||||
.e_anno_only => {},
|
||||
else => return error.WrongExprVariant,
|
||||
}
|
||||
}
|
||||
|
||||
test "e_anno_only can be used in statements" {
|
||||
// This test verifies that e_anno_only expressions can be
|
||||
// used as part of s_decl statements, which is how standalone
|
||||
// type annotations are represented after canonicalization.
|
||||
|
||||
const pattern_idx: CIR.Pattern.Idx = @enumFromInt(0);
|
||||
const expr_idx: CIR.Expr.Idx = @enumFromInt(0);
|
||||
const anno_idx: CIR.Annotation.Idx = @enumFromInt(0);
|
||||
|
||||
const stmt = CIR.Statement{ .s_decl = .{
|
||||
.pattern = pattern_idx,
|
||||
.expr = expr_idx,
|
||||
.anno = anno_idx,
|
||||
} };
|
||||
|
||||
// Verify the statement was created correctly
|
||||
switch (stmt) {
|
||||
.s_decl => |decl| {
|
||||
try testing.expect(decl.anno != null);
|
||||
},
|
||||
else => return error.WrongStatementType,
|
||||
}
|
||||
}
|
||||
|
|
@ -712,7 +712,7 @@ test "NodeStore round trip - Diagnostics" {
|
|||
});
|
||||
|
||||
try diagnostics.append(gpa, CIR.Diagnostic{
|
||||
.type_var_ending_in_underscore = .{
|
||||
.type_var_starting_with_dollar = .{
|
||||
.name = rand_ident_idx(),
|
||||
.suggested_name = rand_ident_idx(),
|
||||
.region = rand_region(),
|
||||
|
|
|
|||
|
|
@ -2976,6 +2976,21 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, rank: types_mod.Rank, expected
|
|||
.e_ellipsis => {
|
||||
try self.updateVar(expr_var, .{ .flex = Flex.init() }, rank);
|
||||
},
|
||||
.e_anno_only => {
|
||||
// For annotation-only expressions, the type comes from the annotation.
|
||||
// This case should only occur when the expression has an annotation (which is
|
||||
// enforced during canonicalization), so the expected type should be set.
|
||||
// The type will be unified with the expected type in the code below.
|
||||
switch (expected) {
|
||||
.no_expectation => {
|
||||
// This shouldn't happen since we always create e_anno_only with an annotation
|
||||
try self.updateVar(expr_var, .err, rank);
|
||||
},
|
||||
.expected => {
|
||||
// The expr_var will be unified with the annotation var below
|
||||
},
|
||||
}
|
||||
},
|
||||
.e_runtime_error => {
|
||||
try self.updateVar(expr_var, .err, rank);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -751,7 +751,33 @@ pub const BuildEnv = struct {
|
|||
// Read source
|
||||
const file_abs = try std.fs.path.resolve(self.gpa, &.{file_path});
|
||||
defer self.gpa.free(file_abs);
|
||||
const src = try std.fs.cwd().readFileAlloc(self.gpa, file_abs, std.math.maxInt(usize));
|
||||
const src = std.fs.cwd().readFileAlloc(self.gpa, file_abs, std.math.maxInt(usize)) catch |err| {
|
||||
const report = blk: switch (err) {
|
||||
error.FileNotFound => {
|
||||
var report = Report.init(self.gpa, "FILE NOT FOUND", .fatal);
|
||||
try report.document.addText("I could not find the file ");
|
||||
try report.document.addAnnotated(file_abs, .path);
|
||||
try report.document.addLineBreak();
|
||||
try report.document.addText("Make sure the file exists and you do not have any typos in its name or path.");
|
||||
break :blk report;
|
||||
},
|
||||
|
||||
else => {
|
||||
var report = Report.init(self.gpa, "COULD NOT READ FILE", .fatal);
|
||||
try report.document.addText("I could not read the file ");
|
||||
try report.document.addAnnotated(file_abs, .path);
|
||||
try report.document.addLineBreak();
|
||||
try report.document.addText("I did get the following error: ");
|
||||
try report.addErrorMessage(@errorName(err));
|
||||
try report.document.addText("Make sure the file can be read.");
|
||||
break :blk report;
|
||||
},
|
||||
};
|
||||
self.sink.emitReport("main", file_abs, report);
|
||||
try self.sink.buildOrder(&[_][]const u8{"main"}, &[_][]const u8{file_abs}, &[_]u32{0});
|
||||
self.sink.tryEmit();
|
||||
return err;
|
||||
};
|
||||
defer self.gpa.free(src);
|
||||
|
||||
var env = try ModuleEnv.init(self.gpa, src);
|
||||
|
|
|
|||
|
|
@ -435,6 +435,7 @@ pub const ComptimeEvaluator = struct {
|
|||
try self.interpreter.bindings.append(.{
|
||||
.pattern_idx = def.pattern,
|
||||
.value = value,
|
||||
.expr_idx = def.expr,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ pub const Interpreter = struct {
|
|||
return std.mem.eql(types.Var, a.args_ptr[0..a.args_len], b.args_ptr[0..b.args_len]);
|
||||
}
|
||||
};
|
||||
const Binding = struct { pattern_idx: can.CIR.Pattern.Idx, value: StackValue };
|
||||
const Binding = struct { pattern_idx: can.CIR.Pattern.Idx, value: StackValue, expr_idx: can.CIR.Expr.Idx };
|
||||
const DefInProgress = struct {
|
||||
pattern_idx: can.CIR.Pattern.Idx,
|
||||
expr_idx: can.CIR.Expr.Idx,
|
||||
|
|
@ -316,7 +316,7 @@ pub const Interpreter = struct {
|
|||
while (j < params.len) : (j += 1) {
|
||||
const sorted_idx = args_accessor.findElementIndexByOriginal(j) orelse j;
|
||||
const arg_value = try args_accessor.getElement(sorted_idx);
|
||||
const matched = try self.patternMatchesBind(params[j], arg_value, param_rt_vars[j], roc_ops, &temp_binds);
|
||||
const matched = try self.patternMatchesBind(params[j], arg_value, param_rt_vars[j], roc_ops, &temp_binds, @enumFromInt(0));
|
||||
if (!matched) return error.TypeMismatch;
|
||||
}
|
||||
}
|
||||
|
|
@ -402,7 +402,7 @@ pub const Interpreter = struct {
|
|||
.source_env = self_interp.env,
|
||||
};
|
||||
}
|
||||
try self_interp.bindings.append(.{ .pattern_idx = patt_idx, .value = ph });
|
||||
try self_interp.bindings.append(.{ .pattern_idx = patt_idx, .value = ph, .expr_idx = rhs_expr });
|
||||
}
|
||||
};
|
||||
switch (stmt) {
|
||||
|
|
@ -442,7 +442,7 @@ pub const Interpreter = struct {
|
|||
const val = try self.evalExprMinimal(d.expr, roc_ops, expr_rt_var);
|
||||
defer val.decref(&self.runtime_layout_store, roc_ops);
|
||||
|
||||
if (!try self.patternMatchesBind(d.pattern, val, expr_rt_var, roc_ops, &temp_binds)) {
|
||||
if (!try self.patternMatchesBind(d.pattern, val, expr_rt_var, roc_ops, &temp_binds, d.expr)) {
|
||||
return error.TypeMismatch;
|
||||
}
|
||||
|
||||
|
|
@ -463,7 +463,7 @@ pub const Interpreter = struct {
|
|||
const val = try self.evalExprMinimal(v.expr, roc_ops, expr_rt_var);
|
||||
defer val.decref(&self.runtime_layout_store, roc_ops);
|
||||
|
||||
if (!try self.patternMatchesBind(v.pattern_idx, val, expr_rt_var, roc_ops, &temp_binds)) {
|
||||
if (!try self.patternMatchesBind(v.pattern_idx, val, expr_rt_var, roc_ops, &temp_binds, v.expr)) {
|
||||
return error.TypeMismatch;
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +558,7 @@ pub const Interpreter = struct {
|
|||
temp_binds.deinit();
|
||||
}
|
||||
|
||||
if (!try self.patternMatchesBind(for_stmt.patt, elem_value, patt_rt_var, roc_ops, &temp_binds)) {
|
||||
if (!try self.patternMatchesBind(for_stmt.patt, elem_value, patt_rt_var, roc_ops, &temp_binds, @enumFromInt(0))) {
|
||||
elem_value.decref(&self.runtime_layout_store, roc_ops);
|
||||
return error.TypeMismatch;
|
||||
}
|
||||
|
|
@ -1294,7 +1294,7 @@ pub const Interpreter = struct {
|
|||
|
||||
for (patterns) |bp_idx| {
|
||||
self.trimBindingList(&temp_binds, 0, roc_ops);
|
||||
if (!try self.patternMatchesBind(self.env.store.getMatchBranchPattern(bp_idx).pattern, scrutinee, scrutinee_rt_var, roc_ops, &temp_binds)) {
|
||||
if (!try self.patternMatchesBind(self.env.store.getMatchBranchPattern(bp_idx).pattern, scrutinee, scrutinee_rt_var, roc_ops, &temp_binds, @enumFromInt(0))) {
|
||||
self.trimBindingList(&temp_binds, 0, roc_ops);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1376,6 +1376,38 @@ pub const Interpreter = struct {
|
|||
}
|
||||
return value;
|
||||
},
|
||||
.e_anno_only => |_| {
|
||||
// This represents a value that only has a type annotation, no implementation
|
||||
const ct_var = can.ModuleEnv.varFrom(expr_idx);
|
||||
const rt_var = try self.translateTypeVar(self.env, ct_var);
|
||||
const anno_layout = try self.getRuntimeLayout(rt_var);
|
||||
|
||||
if (anno_layout.tag == .closure) {
|
||||
// Function type: Build a closure-like value that will crash when called
|
||||
const value = try self.pushRaw(anno_layout, 0);
|
||||
self.registerDefValue(expr_idx, value);
|
||||
// Initialize the closure header with the e_anno_only expr itself as the body
|
||||
// This serves as a marker that will be detected during call evaluation
|
||||
if (value.ptr) |ptr| {
|
||||
const header: *layout.Closure = @ptrCast(@alignCast(ptr));
|
||||
header.* = .{
|
||||
.body_idx = expr_idx, // Point to self (the e_anno_only expression)
|
||||
.params = .{ .span = .{ .start = 0, .len = 0 } }, // No params
|
||||
.captures_pattern_idx = @enumFromInt(@as(u32, 0)),
|
||||
.captures_layout_idx = anno_layout.data.closure.captures_layout_idx,
|
||||
.lambda_expr_idx = expr_idx,
|
||||
.source_env = self.env,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
// Non-function type: Create a value that will be marked as annotation-only
|
||||
// We'll detect this during lookup and crash then
|
||||
const value = try self.pushRaw(anno_layout, 0);
|
||||
self.registerDefValue(expr_idx, value);
|
||||
return value;
|
||||
}
|
||||
},
|
||||
.e_closure => |cls| {
|
||||
// Build a closure value with concrete captures. The closure references a lambda.
|
||||
const lam_expr = self.env.store.getExpr(cls.lambda_idx);
|
||||
|
|
@ -1544,6 +1576,13 @@ pub const Interpreter = struct {
|
|||
self.bindings.shrinkRetainingCapacity(saved_bindings_len);
|
||||
}
|
||||
|
||||
// Check if this is an annotation-only function (body points to e_anno_only)
|
||||
const body_expr = self.env.store.getExpr(header.body_idx);
|
||||
if (body_expr == .e_anno_only) {
|
||||
self.triggerCrash("This function has no implementation. It is only a type annotation for now.", false, roc_ops);
|
||||
return error.Crash;
|
||||
}
|
||||
|
||||
const params = self.env.store.slicePatterns(header.params);
|
||||
if (params.len != arg_indices.len) return error.TypeMismatch;
|
||||
// Provide closure context for capture lookup during body eval
|
||||
|
|
@ -1551,7 +1590,7 @@ pub const Interpreter = struct {
|
|||
defer _ = self.active_closures.pop();
|
||||
var bind_count: usize = 0;
|
||||
while (bind_count < params.len) : (bind_count += 1) {
|
||||
try self.bindings.append(.{ .pattern_idx = params[bind_count], .value = arg_values[bind_count] });
|
||||
try self.bindings.append(.{ .pattern_idx = params[bind_count], .value = arg_values[bind_count], .expr_idx = @enumFromInt(0) });
|
||||
}
|
||||
defer {
|
||||
var k = params.len;
|
||||
|
|
@ -1573,7 +1612,7 @@ pub const Interpreter = struct {
|
|||
if (params.len != arg_indices.len) return error.TypeMismatch;
|
||||
var bind_count: usize = 0;
|
||||
while (bind_count < params.len) : (bind_count += 1) {
|
||||
try self.bindings.append(.{ .pattern_idx = params[bind_count], .value = arg_values[bind_count] });
|
||||
try self.bindings.append(.{ .pattern_idx = params[bind_count], .value = arg_values[bind_count], .expr_idx = @enumFromInt(0) });
|
||||
}
|
||||
defer {
|
||||
var k = params.len;
|
||||
|
|
@ -1795,7 +1834,7 @@ pub const Interpreter = struct {
|
|||
|
||||
var bind_count: usize = 0;
|
||||
while (bind_count < params.len) : (bind_count += 1) {
|
||||
try self.bindings.append(.{ .pattern_idx = params[bind_count], .value = all_args[bind_count] });
|
||||
try self.bindings.append(.{ .pattern_idx = params[bind_count], .value = all_args[bind_count], .expr_idx = @enumFromInt(0) });
|
||||
}
|
||||
defer {
|
||||
var k = params.len;
|
||||
|
|
@ -1816,6 +1855,17 @@ pub const Interpreter = struct {
|
|||
i -= 1;
|
||||
const b = self.bindings.items[i];
|
||||
if (b.pattern_idx == lookup.pattern_idx) {
|
||||
// Check if this binding came from an e_anno_only expression
|
||||
// Skip check for expr_idx == 0 (sentinel for non-def bindings like parameters)
|
||||
const expr_idx_int: u32 = @intFromEnum(b.expr_idx);
|
||||
if (expr_idx_int != 0) {
|
||||
const binding_expr = self.env.store.getExpr(b.expr_idx);
|
||||
if (binding_expr == .e_anno_only and b.value.layout.tag != .closure) {
|
||||
// This is a non-function annotation-only value being looked up
|
||||
self.triggerCrash("This value has no implementation. It is only a type annotation for now.", false, roc_ops);
|
||||
return error.Crash;
|
||||
}
|
||||
}
|
||||
return try self.pushCopy(b.value, roc_ops);
|
||||
}
|
||||
}
|
||||
|
|
@ -3287,24 +3337,25 @@ pub const Interpreter = struct {
|
|||
value_rt_var: types.Var,
|
||||
roc_ops: *RocOps,
|
||||
out_binds: *std.array_list.AlignedManaged(Binding, null),
|
||||
expr_idx: can.CIR.Expr.Idx,
|
||||
) !bool {
|
||||
const pat = self.env.store.getPattern(pattern_idx);
|
||||
switch (pat) {
|
||||
.assign => |_| {
|
||||
// Bind entire value to this pattern
|
||||
const copied = try self.pushCopy(value, roc_ops);
|
||||
try out_binds.append(.{ .pattern_idx = pattern_idx, .value = copied });
|
||||
try out_binds.append(.{ .pattern_idx = pattern_idx, .value = copied, .expr_idx = expr_idx });
|
||||
return true;
|
||||
},
|
||||
.as => |as_pat| {
|
||||
const before = out_binds.items.len;
|
||||
if (!try self.patternMatchesBind(as_pat.pattern, value, value_rt_var, roc_ops, out_binds)) {
|
||||
if (!try self.patternMatchesBind(as_pat.pattern, value, value_rt_var, roc_ops, out_binds, expr_idx)) {
|
||||
self.trimBindingList(out_binds, before, roc_ops);
|
||||
return false;
|
||||
}
|
||||
|
||||
const alias_value = try self.pushCopy(value, roc_ops);
|
||||
try out_binds.append(.{ .pattern_idx = pattern_idx, .value = alias_value });
|
||||
try out_binds.append(.{ .pattern_idx = pattern_idx, .value = alias_value, .expr_idx = expr_idx });
|
||||
return true;
|
||||
},
|
||||
.underscore => return true,
|
||||
|
|
@ -3321,11 +3372,11 @@ pub const Interpreter = struct {
|
|||
},
|
||||
.nominal => |n| {
|
||||
const underlying = self.resolveBaseVar(value_rt_var);
|
||||
return try self.patternMatchesBind(n.backing_pattern, value, underlying.var_, roc_ops, out_binds);
|
||||
return try self.patternMatchesBind(n.backing_pattern, value, underlying.var_, roc_ops, out_binds, expr_idx);
|
||||
},
|
||||
.nominal_external => |n| {
|
||||
const underlying = self.resolveBaseVar(value_rt_var);
|
||||
return try self.patternMatchesBind(n.backing_pattern, value, underlying.var_, roc_ops, out_binds);
|
||||
return try self.patternMatchesBind(n.backing_pattern, value, underlying.var_, roc_ops, out_binds, expr_idx);
|
||||
},
|
||||
.tuple => |tuple_pat| {
|
||||
if (value.layout.tag != .tuple) return false;
|
||||
|
|
@ -3344,7 +3395,7 @@ pub const Interpreter = struct {
|
|||
if (sorted_idx >= accessor.getElementCount()) return false;
|
||||
const elem_value = try accessor.getElement(sorted_idx);
|
||||
const before = out_binds.items.len;
|
||||
const matched = try self.patternMatchesBind(pat_ids[idx], elem_value, elem_vars[idx], roc_ops, out_binds);
|
||||
const matched = try self.patternMatchesBind(pat_ids[idx], elem_value, elem_vars[idx], roc_ops, out_binds, expr_idx);
|
||||
if (!matched) {
|
||||
self.trimBindingList(out_binds, before, roc_ops);
|
||||
return false;
|
||||
|
|
@ -3380,7 +3431,7 @@ pub const Interpreter = struct {
|
|||
while (idx < prefix_len) : (idx += 1) {
|
||||
const elem_value = try accessor.getElement(idx);
|
||||
const before = out_binds.items.len;
|
||||
const matched = try self.patternMatchesBind(non_rest_patterns[idx], elem_value, elem_rt_var, roc_ops, out_binds);
|
||||
const matched = try self.patternMatchesBind(non_rest_patterns[idx], elem_value, elem_rt_var, roc_ops, out_binds, expr_idx);
|
||||
if (!matched) {
|
||||
self.trimBindingList(out_binds, before, roc_ops);
|
||||
return false;
|
||||
|
|
@ -3393,7 +3444,7 @@ pub const Interpreter = struct {
|
|||
const element_idx = total_len - suffix_len + suffix_idx;
|
||||
const elem_value = try accessor.getElement(element_idx);
|
||||
const before = out_binds.items.len;
|
||||
const matched = try self.patternMatchesBind(suffix_pattern_idx, elem_value, elem_rt_var, roc_ops, out_binds);
|
||||
const matched = try self.patternMatchesBind(suffix_pattern_idx, elem_value, elem_rt_var, roc_ops, out_binds, expr_idx);
|
||||
if (!matched) {
|
||||
self.trimBindingList(out_binds, before, roc_ops);
|
||||
return false;
|
||||
|
|
@ -3405,7 +3456,7 @@ pub const Interpreter = struct {
|
|||
const rest_value = try self.makeListSliceValue(list_layout, elem_layout, accessor.list, prefix_len, rest_len);
|
||||
defer rest_value.decref(&self.runtime_layout_store, roc_ops);
|
||||
const before = out_binds.items.len;
|
||||
if (!try self.patternMatchesBind(rest_pat_idx, rest_value, value_rt_var, roc_ops, out_binds)) {
|
||||
if (!try self.patternMatchesBind(rest_pat_idx, rest_value, value_rt_var, roc_ops, out_binds, expr_idx)) {
|
||||
self.trimBindingList(out_binds, before, roc_ops);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3418,7 +3469,7 @@ pub const Interpreter = struct {
|
|||
while (idx < non_rest_patterns.len) : (idx += 1) {
|
||||
const elem_value = try accessor.getElement(idx);
|
||||
const before = out_binds.items.len;
|
||||
const matched = try self.patternMatchesBind(non_rest_patterns[idx], elem_value, elem_rt_var, roc_ops, out_binds);
|
||||
const matched = try self.patternMatchesBind(non_rest_patterns[idx], elem_value, elem_rt_var, roc_ops, out_binds, expr_idx);
|
||||
if (!matched) {
|
||||
self.trimBindingList(out_binds, before, roc_ops);
|
||||
return false;
|
||||
|
|
@ -3447,7 +3498,7 @@ pub const Interpreter = struct {
|
|||
};
|
||||
|
||||
const before = out_binds.items.len;
|
||||
if (!try self.patternMatchesBind(inner_pattern_idx, field_value, field_var, roc_ops, out_binds)) {
|
||||
if (!try self.patternMatchesBind(inner_pattern_idx, field_value, field_var, roc_ops, out_binds, expr_idx)) {
|
||||
self.trimBindingList(out_binds, before, roc_ops);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3493,7 +3544,7 @@ pub const Interpreter = struct {
|
|||
};
|
||||
|
||||
if (arg_patterns.len == 1) {
|
||||
if (!try self.patternMatchesBind(arg_patterns[0], payload_value, arg_vars[0], roc_ops, out_binds)) {
|
||||
if (!try self.patternMatchesBind(arg_patterns[0], payload_value, arg_vars[0], roc_ops, out_binds, expr_idx)) {
|
||||
self.trimBindingList(out_binds, start_len, roc_ops);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3519,7 +3570,7 @@ pub const Interpreter = struct {
|
|||
return false;
|
||||
}
|
||||
const elem_val = try payload_tuple.getElement(sorted_idx);
|
||||
if (!try self.patternMatchesBind(arg_patterns[j], elem_val, arg_vars[j], roc_ops, out_binds)) {
|
||||
if (!try self.patternMatchesBind(arg_patterns[j], elem_val, arg_vars[j], roc_ops, out_binds, expr_idx)) {
|
||||
self.trimBindingList(out_binds, start_len, roc_ops);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,4 +40,5 @@ test "eval tests" {
|
|||
std.testing.refAllDecls(@import("test/helpers.zig"));
|
||||
std.testing.refAllDecls(@import("test/interpreter_style_test.zig"));
|
||||
std.testing.refAllDecls(@import("test/interpreter_polymorphism_test.zig"));
|
||||
std.testing.refAllDecls(@import("test/anno_only_interp_test.zig"));
|
||||
}
|
||||
|
|
|
|||
38
src/eval/test/anno_only_interp_test.zig
Normal file
38
src/eval/test/anno_only_interp_test.zig
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//! Tests for e_anno_only expression evaluation in the interpreter
|
||||
//!
|
||||
//! NOTE: Standalone type annotations (e_anno_only) are only valid at the module/file level,
|
||||
//! not in expression contexts. The current test framework (parseAndCanonicalizeExpr) parses
|
||||
//! expressions, not full modules, so we cannot test this feature with unit tests here.
|
||||
//!
|
||||
//! The feature implementation is complete and can be tested manually with .roc files:
|
||||
//!
|
||||
//! Example (should crash when called):
|
||||
//! foo : Str -> Str
|
||||
//! result = foo "hello"
|
||||
//! result
|
||||
//!
|
||||
//! Example (should crash when looked up):
|
||||
//! bar : Str
|
||||
//! result = bar
|
||||
//! result
|
||||
//!
|
||||
//! The interpreter correctly handles:
|
||||
//! - Function-typed e_anno_only: creates a closure that crashes when called (IMPLEMENTED)
|
||||
//! - Non-function-typed e_anno_only: crashes when the value is looked up (IMPLEMENTED)
|
||||
//!
|
||||
//! Integration tests or REPL tests would be needed to verify this behavior automatically.
|
||||
|
||||
const std = @import("std");
|
||||
const helpers = @import("helpers.zig");
|
||||
const testing = std.testing;
|
||||
|
||||
// Placeholder test to keep the file valid
|
||||
test "e_anno_only implementation exists" {
|
||||
// This is just a placeholder. Real testing requires module-level code,
|
||||
// which the expression-based test framework doesn't support.
|
||||
// The implementation is in interpreter.zig:
|
||||
// - Lines 1379-1409: e_anno_only evaluation (creates placeholder values)
|
||||
// - Lines 1579-1584: function call crash check (crashes when function is called)
|
||||
// - Lines 1858-1868: lookup crash check (crashes when non-function value is looked up)
|
||||
try testing.expect(true);
|
||||
}
|
||||
|
|
@ -1451,6 +1451,38 @@ pub const Tokenizer = struct {
|
|||
}
|
||||
},
|
||||
|
||||
'$' => {
|
||||
const next = self.cursor.peekAt(1);
|
||||
if (next) |n| {
|
||||
if (n >= 'a' and n <= 'z') {
|
||||
// Dollar sign followed by lowercase letter - reusable identifier
|
||||
self.cursor.pos += 1;
|
||||
const tag = self.cursor.chompIdentLower();
|
||||
if (tag == .LowerIdent or tag == .MalformedUnicodeIdent) {
|
||||
try self.pushTokenInternedHere(gpa, tag, start, start);
|
||||
} else {
|
||||
try self.pushTokenNormalHere(gpa, tag, start);
|
||||
}
|
||||
} else if (n >= 'A' and n <= 'Z') {
|
||||
// Dollar sign followed by uppercase letter - reusable identifier
|
||||
var tag: Token.Tag = .UpperIdent;
|
||||
self.cursor.pos += 1;
|
||||
if (!self.cursor.chompIdentGeneral()) {
|
||||
tag = .MalformedUnicodeIdent;
|
||||
}
|
||||
try self.pushTokenInternedHere(gpa, tag, start, start);
|
||||
} else {
|
||||
// Dollar sign not followed by a letter - invalid
|
||||
self.cursor.pos += 1;
|
||||
try self.pushTokenNormalHere(gpa, .MalformedUnknownToken, start);
|
||||
}
|
||||
} else {
|
||||
// Dollar sign at end of file
|
||||
self.cursor.pos += 1;
|
||||
try self.pushTokenNormalHere(gpa, .MalformedUnknownToken, start);
|
||||
}
|
||||
},
|
||||
|
||||
// Numbers starting with 0-9
|
||||
'0'...'9' => {
|
||||
const tag = self.cursor.chompNumber();
|
||||
|
|
@ -2339,6 +2371,15 @@ test "tokenizer" {
|
|||
.StringPart,
|
||||
},
|
||||
);
|
||||
|
||||
// Test dollar sign prefix for reusable identifiers
|
||||
try testTokenization(gpa, "$foo", &[_]Token.Tag{.LowerIdent});
|
||||
try testTokenization(gpa, "$Foo", &[_]Token.Tag{.UpperIdent});
|
||||
try testTokenization(gpa, "$foo123", &[_]Token.Tag{.LowerIdent});
|
||||
try testTokenization(gpa, "$", &[_]Token.Tag{.MalformedUnknownToken});
|
||||
try testTokenization(gpa, "$123", &[_]Token.Tag{ .MalformedUnknownToken, .Int });
|
||||
try testTokenization(gpa, "$ ", &[_]Token.Tag{.MalformedUnknownToken});
|
||||
try testTokenization(gpa, "$foo $bar", &[_]Token.Tag{ .LowerIdent, .LowerIdent });
|
||||
}
|
||||
|
||||
test "tokenizer with invalid UTF-8" {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,28 @@ type=expr
|
|||
}
|
||||
~~~
|
||||
# EXPECTED
|
||||
DUPLICATE DEFINITION - ann_effectful_fn.md:3:5:3:19
|
||||
UNUSED VALUE - ann_effectful_fn.md:2:35:2:39
|
||||
UNUSED VALUE - ann_effectful_fn.md:2:40:2:53
|
||||
# PROBLEMS
|
||||
**DUPLICATE DEFINITION**
|
||||
The name `launchTheNukes` is being redeclared in this scope.
|
||||
|
||||
The redeclaration is here:
|
||||
**ann_effectful_fn.md:3:5:3:19:**
|
||||
```roc
|
||||
launchTheNukes = |{}| ...
|
||||
```
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
But `launchTheNukes` was already defined here:
|
||||
**ann_effectful_fn.md:2:5:2:34:**
|
||||
```roc
|
||||
launchTheNukes : {} => Result Bool LaunchNukeErr
|
||||
```
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNUSED VALUE**
|
||||
This expression produces a value, but it's not being used:
|
||||
**ann_effectful_fn.md:2:35:2:39:**
|
||||
|
|
@ -81,10 +100,9 @@ EndOfFile,
|
|||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(e-block
|
||||
(s-type-anno (name "launchTheNukes")
|
||||
(ty-fn (effectful true)
|
||||
(ty-record)
|
||||
(ty-lookup (name "Result") (external-module "Result"))))
|
||||
(s-let
|
||||
(p-assign (ident "launchTheNukes"))
|
||||
(e-anno-only))
|
||||
(s-expr
|
||||
(e-tag (name "Bool")))
|
||||
(s-expr
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ MALFORMED TYPE - record_builder.md:2:8:2:9
|
|||
UNRECOGNIZED SYNTAX - record_builder.md:2:9:2:10
|
||||
MALFORMED TYPE - record_builder.md:3:8:3:9
|
||||
UNRECOGNIZED SYNTAX - record_builder.md:3:9:3:10
|
||||
UNUSED VARIABLE - record_builder.md:2:5:2:9
|
||||
UNUSED VARIABLE - record_builder.md:3:5:3:9
|
||||
# PROBLEMS
|
||||
**UNEXPECTED TOKEN IN EXPRESSION**
|
||||
The token **<-** is not expected in an expression.
|
||||
|
|
@ -142,6 +144,30 @@ I don't recognize this syntax.
|
|||
|
||||
This might be a syntax error, an unsupported language feature, or a typo.
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `x` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_x` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**record_builder.md:2:5:2:9:**
|
||||
```roc
|
||||
x: 5,
|
||||
```
|
||||
^^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `y` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_y` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**record_builder.md:3:5:3:9:**
|
||||
```roc
|
||||
y: 0,
|
||||
```
|
||||
^^^^
|
||||
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
OpenCurly,UpperIdent,NoSpaceDotUpperIdent,NoSpaceDotLowerIdent,OpBackArrow,
|
||||
|
|
@ -181,12 +207,14 @@ EndOfFile,
|
|||
(e-runtime-error (tag "ident_not_in_scope")))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-type-anno (name "x")
|
||||
(ty-malformed))
|
||||
(s-let
|
||||
(p-assign (ident "x"))
|
||||
(e-anno-only))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-type-anno (name "y")
|
||||
(ty-malformed))
|
||||
(s-let
|
||||
(p-assign (ident "y"))
|
||||
(e-anno-only))
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
~~~
|
||||
# TYPES
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ UNEXPECTED TOKEN IN TYPE ANNOTATION - record_field_update_error.md:1:17:1:19
|
|||
UNDEFINED VARIABLE - record_field_update_error.md:1:3:1:9
|
||||
UNRECOGNIZED SYNTAX - record_field_update_error.md:1:10:1:11
|
||||
MALFORMED TYPE - record_field_update_error.md:1:17:1:19
|
||||
UNUSED VARIABLE - record_field_update_error.md:1:12:1:19
|
||||
# PROBLEMS
|
||||
**UNEXPECTED TOKEN IN EXPRESSION**
|
||||
The token **&** is not expected in an expression.
|
||||
|
|
@ -68,6 +69,18 @@ This type annotation is malformed or contains invalid syntax.
|
|||
^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `age` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_age` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**record_field_update_error.md:1:12:1:19:**
|
||||
```roc
|
||||
{ person & age: 31 }
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
OpenCurly,LowerIdent,OpAmpersand,LowerIdent,OpColon,Int,CloseCurly,
|
||||
|
|
@ -96,8 +109,9 @@ EndOfFile,
|
|||
(e-runtime-error (tag "ident_not_in_scope")))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-type-anno (name "age")
|
||||
(ty-malformed))
|
||||
(s-let
|
||||
(p-assign (ident "age"))
|
||||
(e-anno-only))
|
||||
(e-empty_record))
|
||||
~~~
|
||||
# TYPES
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ main = |_| {
|
|||
# EXPECTED
|
||||
UNEXPECTED TOKEN IN EXPRESSION - let_polymorphism_records.md:19:50:19:51
|
||||
UNRECOGNIZED SYNTAX - let_polymorphism_records.md:19:50:19:51
|
||||
UNUSED VARIABLE - let_polymorphism_records.md:19:52:19:67
|
||||
UNUSED VARIABLE - let_polymorphism_records.md:19:27:19:36
|
||||
UNUSED VALUE - let_polymorphism_records.md:19:40:19:49
|
||||
# PROBLEMS
|
||||
|
|
@ -70,6 +71,18 @@ update_data = |container, new_value| { container & data: new_value }
|
|||
|
||||
This might be a syntax error, an unsupported language feature, or a typo.
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `data` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_data` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**let_polymorphism_records.md:19:52:19:67:**
|
||||
```roc
|
||||
update_data = |container, new_value| { container & data: new_value }
|
||||
```
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `new_value` is not used anywhere in your code.
|
||||
|
||||
|
|
@ -353,8 +366,9 @@ main = |_| {
|
|||
(p-assign (ident "container"))))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-type-anno (name "data")
|
||||
(ty-rigid-var (name "new_value")))
|
||||
(s-let
|
||||
(p-assign (ident "data"))
|
||||
(e-anno-only))
|
||||
(e-empty_record))))
|
||||
(d-let
|
||||
(p-assign (ident "updated_int"))
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ UNEXPECTED TOKEN IN TYPE ANNOTATION - error_malformed_syntax_2.md:1:8:1:10
|
|||
UNEXPECTED TOKEN IN EXPRESSION - error_malformed_syntax_2.md:1:10:1:11
|
||||
MALFORMED TYPE - error_malformed_syntax_2.md:1:8:1:10
|
||||
UNRECOGNIZED SYNTAX - error_malformed_syntax_2.md:1:10:1:11
|
||||
UNUSED VARIABLE - error_malformed_syntax_2.md:1:3:1:10
|
||||
UNUSED VARIABLE - error_malformed_syntax_2.md:1:12:1:16
|
||||
# PROBLEMS
|
||||
**UNEXPECTED TOKEN IN TYPE ANNOTATION**
|
||||
|
|
@ -57,6 +58,18 @@ I don't recognize this syntax.
|
|||
|
||||
This might be a syntax error, an unsupported language feature, or a typo.
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `age` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_age` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**error_malformed_syntax_2.md:1:3:1:10:**
|
||||
```roc
|
||||
{ age: 42, name = "Alice" }
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `name` is not used anywhere in your code.
|
||||
|
||||
|
|
@ -96,8 +109,9 @@ EndOfFile,
|
|||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(e-block
|
||||
(s-type-anno (name "age")
|
||||
(ty-malformed))
|
||||
(s-let
|
||||
(p-assign (ident "age"))
|
||||
(e-anno-only))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-let
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ UNEXPECTED TOKEN IN EXPRESSION - record_different_fields_error.md:4:15:4:16
|
|||
UNEXPECTED TOKEN IN EXPRESSION - record_different_fields_error.md:4:25:4:26
|
||||
UNEXPECTED TOKEN IN EXPRESSION - record_different_fields_error.md:5:15:5:16
|
||||
UNEXPECTED TOKEN IN EXPRESSION - record_different_fields_error.md:5:24:5:25
|
||||
UNEXPECTED TOKEN IN EXPRESSION - record_different_fields_error.md:6:10:6:11
|
||||
UNEXPECTED TOKEN IN TYPE ANNOTATION - record_different_fields_error.md:6:20:6:21
|
||||
UNEXPECTED TOKEN IN EXPRESSION - record_different_fields_error.md:6:21:6:27
|
||||
UNEXPECTED TOKEN IN EXPRESSION - record_different_fields_error.md:6:27:6:28
|
||||
|
|
@ -50,7 +49,6 @@ UNDEFINED VARIABLE - record_different_fields_error.md:5:11:5:15
|
|||
UNRECOGNIZED SYNTAX - record_different_fields_error.md:5:15:5:16
|
||||
UNRECOGNIZED SYNTAX - record_different_fields_error.md:5:24:5:25
|
||||
UNDEFINED VARIABLE - record_different_fields_error.md:6:5:6:10
|
||||
UNRECOGNIZED SYNTAX - record_different_fields_error.md:6:10:6:11
|
||||
MALFORMED TYPE - record_different_fields_error.md:6:20:6:21
|
||||
UNRECOGNIZED SYNTAX - record_different_fields_error.md:6:21:6:27
|
||||
UNRECOGNIZED SYNTAX - record_different_fields_error.md:6:27:6:28
|
||||
|
|
@ -59,6 +57,8 @@ UNDEFINED VARIABLE - record_different_fields_error.md:7:5:7:10
|
|||
UNRECOGNIZED SYNTAX - record_different_fields_error.md:7:10:7:17
|
||||
UNRECOGNIZED SYNTAX - record_different_fields_error.md:7:17:7:18
|
||||
UNRECOGNIZED SYNTAX - record_different_fields_error.md:7:30:7:31
|
||||
UNUSED VARIABLE - record_different_fields_error.md:3:5:3:14
|
||||
UNUSED VARIABLE - record_different_fields_error.md:6:10:6:21
|
||||
UNUSED VALUE - record_different_fields_error.md:4:5:4:15
|
||||
UNUSED VALUE - record_different_fields_error.md:4:17:4:25
|
||||
UNUSED VALUE - record_different_fields_error.md:5:17:5:24
|
||||
|
|
@ -196,17 +196,6 @@ Expressions can be identifiers, literals, function calls, or operators.
|
|||
^
|
||||
|
||||
|
||||
**UNEXPECTED TOKEN IN EXPRESSION**
|
||||
The token **$** is not expected in an expression.
|
||||
Expressions can be identifiers, literals, function calls, or operators.
|
||||
|
||||
**record_different_fields_error.md:6:10:6:11:**
|
||||
```roc
|
||||
field$special: "dollar",
|
||||
```
|
||||
^
|
||||
|
||||
|
||||
**UNEXPECTED TOKEN IN TYPE ANNOTATION**
|
||||
The token **"** is not expected in a type annotation.
|
||||
Type annotations should contain types like _Str_, _Num a_, or _List U64_.
|
||||
|
|
@ -447,17 +436,6 @@ Is there an `import` or `exposing` missing up-top?
|
|||
^^^^^
|
||||
|
||||
|
||||
**UNRECOGNIZED SYNTAX**
|
||||
I don't recognize this syntax.
|
||||
|
||||
**record_different_fields_error.md:6:10:6:11:**
|
||||
```roc
|
||||
field$special: "dollar",
|
||||
```
|
||||
^
|
||||
|
||||
This might be a syntax error, an unsupported language feature, or a typo.
|
||||
|
||||
**MALFORMED TYPE**
|
||||
This type annotation is malformed or contains invalid syntax.
|
||||
|
||||
|
|
@ -545,6 +523,30 @@ I don't recognize this syntax.
|
|||
|
||||
This might be a syntax error, an unsupported language feature, or a typo.
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `field_` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_field_` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**record_different_fields_error.md:3:5:3:14:**
|
||||
```roc
|
||||
field_: "trailing underscore",
|
||||
```
|
||||
^^^^^^^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `$special` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_$special` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**record_different_fields_error.md:6:10:6:21:**
|
||||
```roc
|
||||
field$special: "dollar",
|
||||
```
|
||||
^^^^^^^^^^^
|
||||
|
||||
|
||||
**UNUSED VALUE**
|
||||
This expression produces a value, but it's not being used:
|
||||
**record_different_fields_error.md:4:5:4:15:**
|
||||
|
|
@ -596,7 +598,7 @@ NamedUnderscore,OpColon,StringStart,StringPart,StringEnd,Comma,
|
|||
LowerIdent,OpColon,StringStart,StringPart,StringEnd,Comma,
|
||||
UpperIdent,OpColon,StringStart,StringPart,StringEnd,Comma,
|
||||
LowerIdent,OpUnaryMinus,LowerIdent,OpColon,StringStart,StringPart,StringEnd,Comma,
|
||||
LowerIdent,MalformedUnknownToken,LowerIdent,OpColon,StringStart,StringPart,StringEnd,Comma,
|
||||
LowerIdent,LowerIdent,OpColon,StringStart,StringPart,StringEnd,Comma,
|
||||
LowerIdent,OpaqueName,OpColon,StringStart,StringPart,StringEnd,Comma,
|
||||
CloseCurly,
|
||||
EndOfFile,
|
||||
|
|
@ -628,8 +630,7 @@ EndOfFile,
|
|||
(e-string-part (raw "kebab")))
|
||||
(e-malformed (reason "expr_unexpected_token"))
|
||||
(e-ident (raw "field"))
|
||||
(e-malformed (reason "expr_unexpected_token"))
|
||||
(s-type-anno (name "special")
|
||||
(s-type-anno (name "$special")
|
||||
(ty-malformed (tag "ty_anno_unexpected_token")))
|
||||
(e-malformed (reason "expr_unexpected_token"))
|
||||
(e-malformed (reason "expr_unexpected_token"))
|
||||
|
|
@ -656,7 +657,7 @@ EndOfFile,
|
|||
"kebab"
|
||||
|
||||
field
|
||||
special :
|
||||
$special :
|
||||
|
||||
field
|
||||
"at symbol"
|
||||
|
|
@ -666,16 +667,18 @@ EndOfFile,
|
|||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(e-block
|
||||
(s-type-anno (name "_privateField")
|
||||
(ty-malformed))
|
||||
(s-let
|
||||
(p-assign (ident "_privateField"))
|
||||
(e-anno-only))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-type-anno (name "field_")
|
||||
(ty-malformed))
|
||||
(s-let
|
||||
(p-assign (ident "field_"))
|
||||
(e-anno-only))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-expr
|
||||
|
|
@ -705,10 +708,9 @@ EndOfFile,
|
|||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "ident_not_in_scope")))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-type-anno (name "special")
|
||||
(ty-malformed))
|
||||
(s-let
|
||||
(p-assign (ident "$special"))
|
||||
(e-anno-only))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-expr
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ UNRECOGNIZED SYNTAX - record_different_fields_reserved_error.md:7:5:7:7
|
|||
UNRECOGNIZED SYNTAX - record_different_fields_reserved_error.md:7:7:7:8
|
||||
DOES NOT EXIST - record_different_fields_reserved_error.md:7:9:7:19
|
||||
UNRECOGNIZED SYNTAX - record_different_fields_reserved_error.md:7:19:7:20
|
||||
UNUSED VARIABLE - record_different_fields_reserved_error.md:3:5:3:12
|
||||
UNUSED VALUE - record_different_fields_reserved_error.md:4:13:4:29
|
||||
UNUSED VALUE - record_different_fields_reserved_error.md:5:13:5:26
|
||||
# PROBLEMS
|
||||
|
|
@ -424,6 +425,18 @@ I don't recognize this syntax.
|
|||
|
||||
This might be a syntax error, an unsupported language feature, or a typo.
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
Variable `when` is not used anywhere in your code.
|
||||
|
||||
If you don't need this variable, prefix it with an underscore like `_when` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**record_different_fields_reserved_error.md:3:5:3:12:**
|
||||
```roc
|
||||
when: "pattern match",
|
||||
```
|
||||
^^^^^^^
|
||||
|
||||
|
||||
**UNUSED VALUE**
|
||||
This expression produces a value, but it's not being used:
|
||||
**record_different_fields_reserved_error.md:4:13:4:29:**
|
||||
|
|
@ -509,8 +522,9 @@ EndOfFile,
|
|||
(e-block
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-type-anno (name "when")
|
||||
(ty-malformed))
|
||||
(s-let
|
||||
(p-assign (ident "when"))
|
||||
(e-anno-only))
|
||||
(s-expr
|
||||
(e-runtime-error (tag "expr_not_canonicalized")))
|
||||
(s-expr
|
||||
|
|
|
|||
|
|
@ -67,14 +67,9 @@ process_user! : { name : Str, age : } => Str
|
|||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir
|
||||
(s-type-anno (name "process_user!")
|
||||
(ty-fn (effectful true)
|
||||
(ty-record
|
||||
(field (field "name")
|
||||
(ty-lookup (name "Str") (external-module "Str")))
|
||||
(field (field "age")
|
||||
(ty-malformed)))
|
||||
(ty-lookup (name "Str") (external-module "Str")))))
|
||||
(s-let
|
||||
(p-assign (ident "process_user!"))
|
||||
(e-anno-only)))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -39,19 +39,9 @@ NO CHANGE
|
|||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir
|
||||
(s-type-anno (name "create_user!")
|
||||
(ty-fn (effectful true)
|
||||
(ty-lookup (name "Str") (external-module "Str"))
|
||||
(ty-lookup (name "U32") (builtin))
|
||||
(ty-record
|
||||
(field (field "name")
|
||||
(ty-lookup (name "Str") (external-module "Str")))
|
||||
(field (field "age")
|
||||
(ty-lookup (name "U32") (builtin)))
|
||||
(field (field "id")
|
||||
(ty-lookup (name "U64") (builtin)))
|
||||
(field (field "active")
|
||||
(ty-lookup (name "Bool") (external-module "Bool")))))))
|
||||
(s-let
|
||||
(p-assign (ident "create_user!"))
|
||||
(e-anno-only)))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -73,8 +73,9 @@ process_user! :
|
|||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir
|
||||
(s-type-anno (name "process_user!")
|
||||
(ty-malformed)))
|
||||
(s-let
|
||||
(p-assign (ident "process_user!"))
|
||||
(e-anno-only)))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -39,20 +39,9 @@ process_things : { name : Str, age : U32, thing : a }, (a -> Str) -> Str
|
|||
# CANONICALIZE
|
||||
~~~clojure
|
||||
(can-ir
|
||||
(s-type-anno (name "process_things")
|
||||
(ty-fn (effectful false)
|
||||
(ty-record
|
||||
(field (field "name")
|
||||
(ty-lookup (name "Str") (external-module "Str")))
|
||||
(field (field "age")
|
||||
(ty-lookup (name "U32") (builtin)))
|
||||
(field (field "thing")
|
||||
(ty-rigid-var (name "a"))))
|
||||
(ty-parens
|
||||
(ty-fn (effectful false)
|
||||
(ty-rigid-var-lookup (ty-rigid-var (name "a")))
|
||||
(ty-lookup (name "Str") (external-module "Str"))))
|
||||
(ty-lookup (name "Str") (external-module "Str")))))
|
||||
(s-let
|
||||
(p-assign (ident "process_things"))
|
||||
(e-anno-only)))
|
||||
~~~
|
||||
# TYPES
|
||||
~~~clojure
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# META
|
||||
~~~ini
|
||||
description=Comprehensive test of type variable underscore conventions
|
||||
description=Comprehensive test of type variable underscore and dollar sign conventions
|
||||
type=file
|
||||
~~~
|
||||
# SOURCE
|
||||
|
|
@ -11,12 +11,12 @@ app [main] { pf: platform "../basic-cli/platform.roc" }
|
|||
single_use : List(elem) -> Str
|
||||
single_use = |x| "hello"
|
||||
|
||||
# Test 2: TYPE VAR ENDING IN UNDERSCORE - variables should never end with underscore
|
||||
ending_underscore : List(elem_) -> elem_
|
||||
ending_underscore = |list| "default"
|
||||
# Test 2: TYPE VAR STARTING WITH DOLLAR - variables should never start with dollar sign (reusable markers)
|
||||
starting_dollar : List($elem) -> $elem
|
||||
starting_dollar = |list| "default"
|
||||
|
||||
# Test 3: COMBINATION - single-use ending in underscore (both errors)
|
||||
combo_single : List(bad_) -> Str
|
||||
# Test 3: COMBINATION - single-use starting with dollar (both errors)
|
||||
combo_single : List($bad) -> Str
|
||||
combo_single = |x| "combo"
|
||||
|
||||
# Test 4: VALID CASES - these should not generate warnings
|
||||
|
|
@ -30,11 +30,11 @@ main = |x| "done"
|
|||
~~~
|
||||
# EXPECTED
|
||||
UNUSED VARIABLE - type_var_underscore_conventions.md:5:15:5:16
|
||||
UNUSED VARIABLE - type_var_underscore_conventions.md:9:22:9:26
|
||||
UNUSED VARIABLE - type_var_underscore_conventions.md:9:20:9:24
|
||||
UNUSED VARIABLE - type_var_underscore_conventions.md:13:17:13:18
|
||||
UNUSED VARIABLE - type_var_underscore_conventions.md:17:17:17:18
|
||||
UNUSED VARIABLE - type_var_underscore_conventions.md:22:9:22:10
|
||||
TYPE MISMATCH - type_var_underscore_conventions.md:9:28:9:37
|
||||
TYPE MISMATCH - type_var_underscore_conventions.md:9:26:9:35
|
||||
# PROBLEMS
|
||||
**UNUSED VARIABLE**
|
||||
Variable `x` is not used anywhere in your code.
|
||||
|
|
@ -53,11 +53,11 @@ Variable `list` is not used anywhere in your code.
|
|||
|
||||
If you don't need this variable, prefix it with an underscore like `_list` to suppress this warning.
|
||||
The unused variable is declared here:
|
||||
**type_var_underscore_conventions.md:9:22:9:26:**
|
||||
**type_var_underscore_conventions.md:9:20:9:24:**
|
||||
```roc
|
||||
ending_underscore = |list| "default"
|
||||
starting_dollar = |list| "default"
|
||||
```
|
||||
^^^^
|
||||
^^^^
|
||||
|
||||
|
||||
**UNUSED VARIABLE**
|
||||
|
|
@ -98,17 +98,17 @@ main = |x| "done"
|
|||
|
||||
**TYPE MISMATCH**
|
||||
This expression is used in an unexpected way:
|
||||
**type_var_underscore_conventions.md:9:28:9:37:**
|
||||
**type_var_underscore_conventions.md:9:26:9:35:**
|
||||
```roc
|
||||
ending_underscore = |list| "default"
|
||||
starting_dollar = |list| "default"
|
||||
```
|
||||
^^^^^^^^^
|
||||
^^^^^^^^^
|
||||
|
||||
It has the type:
|
||||
_Str_
|
||||
|
||||
But the type annotation says it should have the type:
|
||||
_elem__
|
||||
_$elem_
|
||||
|
||||
# TOKENS
|
||||
~~~zig
|
||||
|
|
@ -154,14 +154,14 @@ EndOfFile,
|
|||
(p-ident (raw "x")))
|
||||
(e-string
|
||||
(e-string-part (raw "hello")))))
|
||||
(s-type-anno (name "ending_underscore")
|
||||
(s-type-anno (name "starting_dollar")
|
||||
(ty-fn
|
||||
(ty-apply
|
||||
(ty (name "List"))
|
||||
(ty-var (raw "elem_")))
|
||||
(ty-var (raw "elem_"))))
|
||||
(ty-var (raw "$elem")))
|
||||
(ty-var (raw "$elem"))))
|
||||
(s-decl
|
||||
(p-ident (raw "ending_underscore"))
|
||||
(p-ident (raw "starting_dollar"))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-ident (raw "list")))
|
||||
|
|
@ -171,7 +171,7 @@ EndOfFile,
|
|||
(ty-fn
|
||||
(ty-apply
|
||||
(ty (name "List"))
|
||||
(ty-var (raw "bad_")))
|
||||
(ty-var (raw "$bad")))
|
||||
(ty (name "Str"))))
|
||||
(s-decl
|
||||
(p-ident (raw "combo_single"))
|
||||
|
|
@ -234,7 +234,7 @@ NO CHANGE
|
|||
(ty-rigid-var (name "elem")))
|
||||
(ty-lookup (name "Str") (external-module "Str")))))
|
||||
(d-let
|
||||
(p-assign (ident "ending_underscore"))
|
||||
(p-assign (ident "starting_dollar"))
|
||||
(e-lambda
|
||||
(args
|
||||
(p-assign (ident "list")))
|
||||
|
|
@ -243,8 +243,8 @@ NO CHANGE
|
|||
(annotation
|
||||
(ty-fn (effectful false)
|
||||
(ty-apply (name "List") (builtin)
|
||||
(ty-rigid-var (name "elem_")))
|
||||
(ty-rigid-var-lookup (ty-rigid-var (name "elem_"))))))
|
||||
(ty-rigid-var (name "$elem")))
|
||||
(ty-rigid-var-lookup (ty-rigid-var (name "$elem"))))))
|
||||
(d-let
|
||||
(p-assign (ident "combo_single"))
|
||||
(e-lambda
|
||||
|
|
@ -255,7 +255,7 @@ NO CHANGE
|
|||
(annotation
|
||||
(ty-fn (effectful false)
|
||||
(ty-apply (name "List") (builtin)
|
||||
(ty-rigid-var (name "bad_")))
|
||||
(ty-rigid-var (name "$bad")))
|
||||
(ty-lookup (name "Str") (external-module "Str")))))
|
||||
(d-let
|
||||
(p-assign (ident "valid_single"))
|
||||
|
|
@ -296,15 +296,15 @@ NO CHANGE
|
|||
(inferred-types
|
||||
(defs
|
||||
(patt (type "List(elem) -> Str"))
|
||||
(patt (type "List(elem_) -> Error"))
|
||||
(patt (type "List(bad_) -> Str"))
|
||||
(patt (type "List($elem) -> Error"))
|
||||
(patt (type "List($bad) -> Str"))
|
||||
(patt (type "List(_elem) -> Str"))
|
||||
(patt (type "elem -> List(elem)"))
|
||||
(patt (type "_arg -> Str")))
|
||||
(expressions
|
||||
(expr (type "List(elem) -> Str"))
|
||||
(expr (type "List(elem_) -> Error"))
|
||||
(expr (type "List(bad_) -> Str"))
|
||||
(expr (type "List($elem) -> Error"))
|
||||
(expr (type "List($bad) -> Str"))
|
||||
(expr (type "List(_elem) -> Str"))
|
||||
(expr (type "elem -> List(elem)"))
|
||||
(expr (type "_arg -> Str"))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue