diff --git a/src/check/Check.zig b/src/check/Check.zig index b17df81ea3..0c71cec649 100644 --- a/src/check/Check.zig +++ b/src/check/Check.zig @@ -692,6 +692,11 @@ pub fn checkPattern(self: *Self, pattern_idx: CIR.Pattern.Idx) std.mem.Allocator /// Check the types for an exprexpression. Returns whether evaluating the expr might perform side effects. pub fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx) std.mem.Allocator.Error!bool { + return self.checkExprWithExpected(expr_idx, null); +} + +/// Check expression with an optional expected type for bidirectional type checking +pub fn checkExprWithExpected(self: *Self, expr_idx: CIR.Expr.Idx, expected_type: ?Var) std.mem.Allocator.Error!bool { const trace = tracy.trace(@src()); defer trace.end(); @@ -705,6 +710,12 @@ pub fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx) std.mem.Allocator.Error!bo // constraints will be checked when unified with expected types. // The type variable for this expression was already created with the // appropriate num_unbound or int_unbound content during canonicalization. + + // If we have an expected type, unify immediately to constrain the literal + if (expected_type) |expected| { + const literal_var = @as(Var, @enumFromInt(@intFromEnum(expr_idx))); + _ = try self.unify(literal_var, expected); + } }, .e_frac_f32 => |_| { // Fractional literals have their type constraints (fits_in_f32, fits_in_dec) @@ -1059,7 +1070,7 @@ pub fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx) std.mem.Allocator.Error!bo _ = try self.unify(closure_var, lambda_var); }, .e_lambda => |lambda| { - does_fx = try self.checkLambdaWithAnno(expr_idx, expr_region, lambda, null); + does_fx = try self.checkLambdaWithAnno(expr_idx, expr_region, lambda, expected_type); }, .e_tuple => |tuple| { // Check tuple elements @@ -1356,8 +1367,9 @@ fn checkLambdaWithAnno( var mb_expected_var: ?Var = null; var mb_expected_func: ?types_mod.Func = null; + var expected_return_type: ?Var = null; - // STEP 1: Apply annotation constraints to parameters FIRST + // STEP 1: Extract expected function type and argument/return types if (anno_type) |anno_var| { mb_expected_var = anno_var; @@ -1372,6 +1384,8 @@ fn checkLambdaWithAnno( if (mb_expected_func) |func| { const expected_args = self.types.sliceVars(func.args); + expected_return_type = func.ret; + if (expected_args.len == arg_patterns.len) { // Constrain parameters with annotation types for (arg_patterns, expected_args) |pattern_idx, expected_arg| { @@ -1384,41 +1398,24 @@ fn checkLambdaWithAnno( } } } - } - - // STEP 2: Check the body - but first set up return type constraints if needed - const return_var = @as(Var, @enumFromInt(@intFromEnum(lambda.body))); - - // For better constraint propagation, especially for numeric literals in lambda bodies, - // we want to establish the return type constraint before checking the body. - // However, to avoid breaking error reporting in other cases, we only do this - // when the expected return type is a concrete base type (not a type variable, nominal type, etc.) - var early_unify = false; - if (mb_expected_func) |func| { - const ret_resolved = self.types.resolveVar(func.ret); - if (ret_resolved.desc.content == .structure) { - // Only do early unification for concrete base types (num, str, etc.) - // Skip nominal types, tag unions, and other complex types - const should_early_unify = switch (ret_resolved.desc.content.structure) { - .num, .str => true, - .nominal_type, .tag_union, .fn_pure, .fn_effectful, .fn_unbound => false, - .list, .box, .tuple, .record, .record_unbound, .record_poly => false, - .empty_record, .empty_tag_union, .list_unbound => false, - }; - - if (should_early_unify) { - // The return type is a concrete base type, unify early for better constraint propagation - _ = try self.unify(return_var, func.ret); - early_unify = true; - } + } else { + // No annotation - just check patterns normally + for (arg_patterns) |pattern_idx| { + try self.checkPattern(pattern_idx); } } - // STEP 3: Check the body - const is_effectful = try self.checkExpr(lambda.body); + // STEP 2: Check the lambda body with expected return type + // This is the key improvement - we pass the expected return type down + // so that numeric literals and nested expressions get properly constrained + const is_effectful = if (expected_return_type) |expected_ret| + try self.checkExprWithExpected(lambda.body, expected_ret) + else + try self.checkExpr(lambda.body); - // STEP 4: Build the function type + // STEP 3: Build the function type const fn_var = ModuleEnv.varFrom(expr_idx); + const return_var = @as(Var, @enumFromInt(@intFromEnum(lambda.body))); if (is_effectful) { _ = try self.types.setVarContent(fn_var, try self.types.mkFuncEffectful(arg_vars, return_var)); @@ -1426,14 +1423,12 @@ fn checkLambdaWithAnno( _ = try self.types.setVarContent(fn_var, try self.types.mkFuncUnbound(arg_vars, return_var)); } - // STEP 5: Validate the function body against the annotation return type (if not done already) - if (mb_expected_func) |func| { - if (!early_unify) { - _ = try self.unify(return_var, func.ret); - } + // STEP 4: Validate the function body against the annotation return type + if (expected_return_type) |expected_ret| { + _ = try self.unify(return_var, expected_ret); } - // STEP 6: Validate the entire function against the annotation + // STEP 5: Validate the entire function against the annotation if (mb_expected_var) |expected_var| { _ = try self.unify(fn_var, expected_var); } @@ -1448,35 +1443,40 @@ fn checkBinopExpr(self: *Self, expr_idx: CIR.Expr.Idx, expr_region: Region, bino const trace = tracy.trace(@src()); defer trace.end(); - var does_fx = try self.checkExpr(binop.lhs); - does_fx = try self.checkExpr(binop.rhs) or does_fx; - switch (binop.op) { .add, .sub, .mul, .div, .rem, .pow, .div_trunc => { - // For now, we'll constrain both operands to be numbers - // In the future, this will use static dispatch based on the lhs type - const lhs_var = @as(Var, @enumFromInt(@intFromEnum(binop.lhs))); - const rhs_var = @as(Var, @enumFromInt(@intFromEnum(binop.rhs))); + // For arithmetic operations, create a shared number type for operands and result const result_var = @as(Var, @enumFromInt(@intFromEnum(expr_idx))); - // Create a fresh number variable for the operation + // Create a fresh number variable that all operands should match const num_content = Content{ .structure = .{ .num = .{ .num_unbound = .{ .sign_needed = false, .bits_needed = 0 } } } }; - const num_var_lhs = try self.freshFromContent(num_content, expr_region); - const num_var_rhs = try self.freshFromContent(num_content, expr_region); - const num_var_result = try self.freshFromContent(num_content, expr_region); + const shared_num_var = try self.freshFromContent(num_content, expr_region); - // Unify lhs, rhs, and result with the number type - _ = try self.unify(num_var_lhs, lhs_var); - _ = try self.unify(num_var_rhs, rhs_var); - _ = try self.unify(result_var, num_var_result); + // Check operands with the shared numeric type as expected + var does_fx = try self.checkExprWithExpected(binop.lhs, shared_num_var); + does_fx = try self.checkExprWithExpected(binop.rhs, shared_num_var) or does_fx; + + // Result should also be of the same numeric type + _ = try self.unify(result_var, shared_num_var); + + return does_fx; }, .lt, .gt, .le, .ge, .eq, .ne => { + // Check operands first + var does_fx = try self.checkExpr(binop.lhs); + does_fx = try self.checkExpr(binop.rhs) or does_fx; + // Comparison operators always return Bool const expr_var = @as(Var, @enumFromInt(@intFromEnum(expr_idx))); const fresh_bool = try self.instantiateVarAnon(ModuleEnv.varFrom(can.Can.BUILTIN_BOOL_TYPE), .{ .explicit = expr_region }); _ = try self.unify(expr_var, fresh_bool); + + return does_fx; }, .@"and" => { + var does_fx = try self.checkExpr(binop.lhs); + does_fx = try self.checkExpr(binop.rhs) or does_fx; + const lhs_fresh_bool = try self.instantiateVarAnon(ModuleEnv.varFrom(can.Can.BUILTIN_BOOL_TYPE), .{ .explicit = expr_region }); const lhs_result = try self.unify(lhs_fresh_bool, @enumFromInt(@intFromEnum(binop.lhs))); self.setDetailIfTypeMismatch(lhs_result, .{ .invalid_bool_binop = .{ @@ -1494,8 +1494,13 @@ fn checkBinopExpr(self: *Self, expr_idx: CIR.Expr.Idx, expr_region: Region, bino .binop = .@"and", } }); } + + return does_fx; }, .@"or" => { + var does_fx = try self.checkExpr(binop.lhs); + does_fx = try self.checkExpr(binop.rhs) or does_fx; + const lhs_fresh_bool = try self.instantiateVarAnon(ModuleEnv.varFrom(can.Can.BUILTIN_BOOL_TYPE), .{ .explicit = expr_region }); const lhs_result = try self.unify(lhs_fresh_bool, @enumFromInt(@intFromEnum(binop.lhs))); self.setDetailIfTypeMismatch(lhs_result, .{ .invalid_bool_binop = .{ @@ -1513,12 +1518,20 @@ fn checkBinopExpr(self: *Self, expr_idx: CIR.Expr.Idx, expr_region: Region, bino .binop = .@"or", } }); } - }, - .pipe_forward => {}, - .null_coalesce => {}, - } - return does_fx; + return does_fx; + }, + .pipe_forward => { + var does_fx = try self.checkExpr(binop.lhs); + does_fx = try self.checkExpr(binop.rhs) or does_fx; + return does_fx; + }, + .null_coalesce => { + var does_fx = try self.checkExpr(binop.lhs); + does_fx = try self.checkExpr(binop.rhs) or does_fx; + return does_fx; + }, + } } fn checkUnaryMinusExpr(self: *Self, expr_idx: CIR.Expr.Idx, expr_region: Region, unary: CIR.Expr.UnaryMinus) Allocator.Error!bool { diff --git a/test/snapshots/can_var_scoping_var_idents.md b/test/snapshots/can_var_scoping_var_idents.md index 9f332eb924..5633503f23 100644 --- a/test/snapshots/can_var_scoping_var_idents.md +++ b/test/snapshots/can_var_scoping_var_idents.md @@ -99,7 +99,7 @@ NO CHANGE ~~~clojure (inferred-types (defs - (patt @4.1-4.9 (type "Num(_size) -> Num(_size2)"))) + (patt @4.1-4.9 (type "_arg -> Num(_size)"))) (expressions - (expr @4.12-10.2 (type "Num(_size) -> Num(_size2)")))) + (expr @4.12-10.2 (type "_arg -> Num(_size)")))) ~~~ diff --git a/test/snapshots/expr/lambda_simple.md b/test/snapshots/expr/lambda_simple.md index d4f6fb8464..61c0e9769d 100644 --- a/test/snapshots/expr/lambda_simple.md +++ b/test/snapshots/expr/lambda_simple.md @@ -40,5 +40,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr @1.1-1.10 (type "Num(_size) -> Num(_size2)")) +(expr @1.1-1.10 (type "_arg -> Num(_size)")) ~~~ diff --git a/test/snapshots/expr/lambda_with_args.md b/test/snapshots/expr/lambda_with_args.md index 1a04156e06..c22c5c8083 100644 --- a/test/snapshots/expr/lambda_with_args.md +++ b/test/snapshots/expr/lambda_with_args.md @@ -43,5 +43,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr @1.1-1.13 (type "Num(_size), Num(_size2) -> Num(_size3)")) +(expr @1.1-1.13 (type "_arg, _arg2 -> Num(_size)")) ~~~ diff --git a/test/snapshots/expr/tag_vs_function_calls.md b/test/snapshots/expr/tag_vs_function_calls.md index 47966baca3..43989ce2fa 100644 --- a/test/snapshots/expr/tag_vs_function_calls.md +++ b/test/snapshots/expr/tag_vs_function_calls.md @@ -166,5 +166,5 @@ CloseCurly(10:1-10:2),EndOfFile(10:2-10:2), ~~~ # TYPES ~~~clojure -(expr @1.1-10.2 (type "{ someTag: [Some(Num(_size))]_others, noneTag: [None]_others2, okTag: Result(Str, err), errTag: Result(ok, Str), addOne: Num(_size2) -> Num(_size3), result: _field, nested: [Some(Error)]_others3, tagList: List([Some(Num(_size4))][None]_others4) }")) +(expr @1.1-10.2 (type "{ someTag: [Some(Num(_size))]_others, noneTag: [None]_others2, okTag: Result(Str, err), errTag: Result(ok, Str), addOne: _arg -> Num(_size2), result: _field, nested: [Some(Error)]_others3, tagList: List([Some(Num(_size3))][None]_others4) }")) ~~~ diff --git a/test/snapshots/function_no_annotation.md b/test/snapshots/function_no_annotation.md index cd86c2265f..679b584ea4 100644 --- a/test/snapshots/function_no_annotation.md +++ b/test/snapshots/function_no_annotation.md @@ -153,13 +153,13 @@ NO CHANGE ~~~clojure (inferred-types (defs - (patt @6.1-6.9 (type "Num(_size), Num(_size2) -> Num(_size3)")) + (patt @6.1-6.9 (type "_arg, _arg2 -> Num(_size)")) (patt @9.1-9.14 (type "_arg -> _ret")) - (patt @12.1-12.9 (type "Num(_size) -> _ret")) + (patt @12.1-12.9 (type "_arg -> _ret")) (patt @14.1-14.6 (type "_a"))) (expressions - (expr @6.12-6.24 (type "Num(_size), Num(_size2) -> Num(_size3)")) + (expr @6.12-6.24 (type "_arg, _arg2 -> Num(_size)")) (expr @9.17-9.36 (type "_arg -> _ret")) - (expr @12.12-12.45 (type "Num(_size) -> _ret")) + (expr @12.12-12.45 (type "_arg -> _ret")) (expr @14.9-14.21 (type "_a")))) ~~~ diff --git a/test/snapshots/lambda_capture/lambda_invalid_references.md b/test/snapshots/lambda_capture/lambda_invalid_references.md index 533a9d7e86..afb35980fa 100644 --- a/test/snapshots/lambda_capture/lambda_invalid_references.md +++ b/test/snapshots/lambda_capture/lambda_invalid_references.md @@ -72,5 +72,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr @1.1-1.14 (type "Num(_size) -> _arg -> Num(_size2)")) +(expr @1.1-1.14 (type "_arg -> _arg2 -> Num(_size)")) ~~~ diff --git a/test/snapshots/lambda_currying_constraint.md b/test/snapshots/lambda_currying_constraint.md new file mode 100644 index 0000000000..96ddacd9d5 --- /dev/null +++ b/test/snapshots/lambda_currying_constraint.md @@ -0,0 +1,295 @@ +# META +~~~ini +description=Lambda currying with polymorphic function constraints - tests if numeric literals in curried functions get properly constrained +type=file +~~~ +# SOURCE +~~~roc +module [makeAdder, curriedAdd, applyTwice] + +# Function that returns a function with polymorphic type +makeAdder : a -> (a -> a) +makeAdder = |x| |y| x + y + +# Should constrain the literal 5 to I64 +curriedAdd : I64 -> I64 +curriedAdd = makeAdder(5) + +# Higher-order function that applies a function twice +applyTwice : (a -> a), a -> a +applyTwice = |f, x| f(f(x)) + +# Should constrain the literal 3 to I64 +addThreeTwice : I64 -> I64 +addThreeTwice = |n| applyTwice(|x| x + 3, n) +~~~ +# EXPECTED +PARSE ERROR - lambda_currying_constraint.md:12:22:12:23 +PARSE ERROR - lambda_currying_constraint.md:12:24:12:25 +PARSE ERROR - lambda_currying_constraint.md:12:26:12:28 +PARSE ERROR - lambda_currying_constraint.md:12:29:12:30 +TYPE MISMATCH - lambda_currying_constraint.md:4:18:4:26 +# PROBLEMS +**PARSE ERROR** +A parsing error occurred: `statement_unexpected_token` +This is an unexpected parsing error. Please check your syntax. + +Here is the problematic code: +**lambda_currying_constraint.md:12:22:12:23:** +```roc +applyTwice : (a -> a), a -> a +``` + ^ + + +**PARSE ERROR** +A parsing error occurred: `statement_unexpected_token` +This is an unexpected parsing error. Please check your syntax. + +Here is the problematic code: +**lambda_currying_constraint.md:12:24:12:25:** +```roc +applyTwice : (a -> a), a -> a +``` + ^ + + +**PARSE ERROR** +A parsing error occurred: `statement_unexpected_token` +This is an unexpected parsing error. Please check your syntax. + +Here is the problematic code: +**lambda_currying_constraint.md:12:26:12:28:** +```roc +applyTwice : (a -> a), a -> a +``` + ^^ + + +**PARSE ERROR** +A parsing error occurred: `statement_unexpected_token` +This is an unexpected parsing error. Please check your syntax. + +Here is the problematic code: +**lambda_currying_constraint.md:12:29:12:30:** +```roc +applyTwice : (a -> a), a -> a +``` + ^ + + +**TYPE MISMATCH** +This expression is used in an unexpected way: +**lambda_currying_constraint.md:4:18:4:26:** +```roc +makeAdder : a -> (a -> a) +``` + ^^^^^^^^ + +It is of type: + _a -> a_ + +But you are trying to use it as: + _a -> Num(_size)_ + +# TOKENS +~~~zig +KwModule(1:1-1:7),OpenSquare(1:8-1:9),LowerIdent(1:9-1:18),Comma(1:18-1:19),LowerIdent(1:20-1:30),Comma(1:30-1:31),LowerIdent(1:32-1:42),CloseSquare(1:42-1:43), +LowerIdent(4:1-4:10),OpColon(4:11-4:12),LowerIdent(4:13-4:14),OpArrow(4:15-4:17),OpenRound(4:18-4:19),LowerIdent(4:19-4:20),OpArrow(4:21-4:23),LowerIdent(4:24-4:25),CloseRound(4:25-4:26), +LowerIdent(5:1-5:10),OpAssign(5:11-5:12),OpBar(5:13-5:14),LowerIdent(5:14-5:15),OpBar(5:15-5:16),OpBar(5:17-5:18),LowerIdent(5:18-5:19),OpBar(5:19-5:20),LowerIdent(5:21-5:22),OpPlus(5:23-5:24),LowerIdent(5:25-5:26), +LowerIdent(8:1-8:11),OpColon(8:12-8:13),UpperIdent(8:14-8:17),OpArrow(8:18-8:20),UpperIdent(8:21-8:24), +LowerIdent(9:1-9:11),OpAssign(9:12-9:13),LowerIdent(9:14-9:23),NoSpaceOpenRound(9:23-9:24),Int(9:24-9:25),CloseRound(9:25-9:26), +LowerIdent(12:1-12:11),OpColon(12:12-12:13),OpenRound(12:14-12:15),LowerIdent(12:15-12:16),OpArrow(12:17-12:19),LowerIdent(12:20-12:21),CloseRound(12:21-12:22),Comma(12:22-12:23),LowerIdent(12:24-12:25),OpArrow(12:26-12:28),LowerIdent(12:29-12:30), +LowerIdent(13:1-13:11),OpAssign(13:12-13:13),OpBar(13:14-13:15),LowerIdent(13:15-13:16),Comma(13:16-13:17),LowerIdent(13:18-13:19),OpBar(13:19-13:20),LowerIdent(13:21-13:22),NoSpaceOpenRound(13:22-13:23),LowerIdent(13:23-13:24),NoSpaceOpenRound(13:24-13:25),LowerIdent(13:25-13:26),CloseRound(13:26-13:27),CloseRound(13:27-13:28), +LowerIdent(16:1-16:14),OpColon(16:15-16:16),UpperIdent(16:17-16:20),OpArrow(16:21-16:23),UpperIdent(16:24-16:27), +LowerIdent(17:1-17:14),OpAssign(17:15-17:16),OpBar(17:17-17:18),LowerIdent(17:18-17:19),OpBar(17:19-17:20),LowerIdent(17:21-17:31),NoSpaceOpenRound(17:31-17:32),OpBar(17:32-17:33),LowerIdent(17:33-17:34),OpBar(17:34-17:35),LowerIdent(17:36-17:37),OpPlus(17:38-17:39),Int(17:40-17:41),Comma(17:41-17:42),LowerIdent(17:43-17:44),CloseRound(17:44-17:45),EndOfFile(17:45-17:45), +~~~ +# PARSE +~~~clojure +(file @1.1-17.45 + (module @1.1-1.43 + (exposes @1.8-1.43 + (exposed-lower-ident @1.9-1.18 + (text "makeAdder")) + (exposed-lower-ident @1.20-1.30 + (text "curriedAdd")) + (exposed-lower-ident @1.32-1.42 + (text "applyTwice")))) + (statements + (s-type-anno @4.1-4.26 (name "makeAdder") + (ty-fn @4.13-4.26 + (ty-var @4.13-4.14 (raw "a")) + (ty-fn @4.19-4.25 + (ty-var @4.19-4.20 (raw "a")) + (ty-var @4.24-4.25 (raw "a"))))) + (s-decl @5.1-5.26 + (p-ident @5.1-5.10 (raw "makeAdder")) + (e-lambda @5.13-5.26 + (args + (p-ident @5.14-5.15 (raw "x"))) + (e-lambda @5.17-5.26 + (args + (p-ident @5.18-5.19 (raw "y"))) + (e-binop @5.21-5.26 (op "+") + (e-ident @5.21-5.22 (raw "x")) + (e-ident @5.25-5.26 (raw "y")))))) + (s-type-anno @8.1-8.24 (name "curriedAdd") + (ty-fn @8.14-8.24 + (ty @8.14-8.17 (name "I64")) + (ty @8.21-8.24 (name "I64")))) + (s-decl @9.1-9.26 + (p-ident @9.1-9.11 (raw "curriedAdd")) + (e-apply @9.14-9.26 + (e-ident @9.14-9.23 (raw "makeAdder")) + (e-int @9.24-9.25 (raw "5")))) + (s-type-anno @12.1-12.22 (name "applyTwice") + (ty-fn @12.15-12.21 + (ty-var @12.15-12.16 (raw "a")) + (ty-var @12.20-12.21 (raw "a")))) + (s-malformed @12.22-12.23 (tag "statement_unexpected_token")) + (s-malformed @12.24-12.25 (tag "statement_unexpected_token")) + (s-malformed @12.26-12.28 (tag "statement_unexpected_token")) + (s-malformed @12.29-12.30 (tag "statement_unexpected_token")) + (s-decl @13.1-13.28 + (p-ident @13.1-13.11 (raw "applyTwice")) + (e-lambda @13.14-13.28 + (args + (p-ident @13.15-13.16 (raw "f")) + (p-ident @13.18-13.19 (raw "x"))) + (e-apply @13.21-13.28 + (e-ident @13.21-13.22 (raw "f")) + (e-apply @13.23-13.27 + (e-ident @13.23-13.24 (raw "f")) + (e-ident @13.25-13.26 (raw "x")))))) + (s-type-anno @16.1-16.27 (name "addThreeTwice") + (ty-fn @16.17-16.27 + (ty @16.17-16.20 (name "I64")) + (ty @16.24-16.27 (name "I64")))) + (s-decl @17.1-17.45 + (p-ident @17.1-17.14 (raw "addThreeTwice")) + (e-lambda @17.17-17.45 + (args + (p-ident @17.18-17.19 (raw "n"))) + (e-apply @17.21-17.45 + (e-ident @17.21-17.31 (raw "applyTwice")) + (e-lambda @17.32-17.41 + (args + (p-ident @17.33-17.34 (raw "x"))) + (e-binop @17.36-17.41 (op "+") + (e-ident @17.36-17.37 (raw "x")) + (e-int @17.40-17.41 (raw "3")))) + (e-ident @17.43-17.44 (raw "n"))))))) +~~~ +# FORMATTED +~~~roc +module [makeAdder, curriedAdd, applyTwice] + +# Function that returns a function with polymorphic type +makeAdder : a -> (a -> a) +makeAdder = |x| |y| x + y + +# Should constrain the literal 5 to I64 +curriedAdd : I64 -> I64 +curriedAdd = makeAdder(5) + +# Higher-order function that applies a function twice +applyTwice : (a -> a) + +applyTwice = |f, x| f(f(x)) + +# Should constrain the literal 3 to I64 +addThreeTwice : I64 -> I64 +addThreeTwice = |n| applyTwice(|x| x + 3, n) +~~~ +# CANONICALIZE +~~~clojure +(can-ir + (d-let + (p-assign @5.1-5.10 (ident "makeAdder")) + (e-lambda @5.13-5.26 + (args + (p-assign @5.14-5.15 (ident "x"))) + (e-closure @5.17-5.26 + (captures + (capture @5.14-5.15 (ident "x"))) + (e-lambda @5.17-5.26 + (args + (p-assign @5.18-5.19 (ident "y"))) + (e-binop @5.21-5.26 (op "add") + (e-lookup-local @5.21-5.22 + (p-assign @5.14-5.15 (ident "x"))) + (e-lookup-local @5.25-5.26 + (p-assign @5.18-5.19 (ident "y"))))))) + (annotation @5.1-5.10 + (declared-type + (ty-fn @4.13-4.26 (effectful false) + (ty-var @4.13-4.14 (name "a")) + (ty-parens @4.18-4.26 + (ty-fn @4.19-4.25 (effectful false) + (ty-var @4.19-4.20 (name "a")) + (ty-var @4.24-4.25 (name "a")))))))) + (d-let + (p-assign @9.1-9.11 (ident "curriedAdd")) + (e-call @9.14-9.26 + (e-lookup-local @9.14-9.23 + (p-assign @5.1-5.10 (ident "makeAdder"))) + (e-int @9.24-9.25 (value "5"))) + (annotation @9.1-9.11 + (declared-type + (ty-fn @8.14-8.24 (effectful false) + (ty @8.14-8.17 (name "I64")) + (ty @8.21-8.24 (name "I64")))))) + (d-let + (p-assign @13.1-13.11 (ident "applyTwice")) + (e-lambda @13.14-13.28 + (args + (p-assign @13.15-13.16 (ident "f")) + (p-assign @13.18-13.19 (ident "x"))) + (e-call @13.21-13.28 + (e-lookup-local @13.21-13.22 + (p-assign @13.15-13.16 (ident "f"))) + (e-call @13.23-13.27 + (e-lookup-local @13.23-13.24 + (p-assign @13.15-13.16 (ident "f"))) + (e-lookup-local @13.25-13.26 + (p-assign @13.18-13.19 (ident "x"))))))) + (d-let + (p-assign @17.1-17.14 (ident "addThreeTwice")) + (e-closure @17.17-17.45 + (captures + (capture @13.1-13.11 (ident "applyTwice"))) + (e-lambda @17.17-17.45 + (args + (p-assign @17.18-17.19 (ident "n"))) + (e-call @17.21-17.45 + (e-lookup-local @17.21-17.31 + (p-assign @13.1-13.11 (ident "applyTwice"))) + (e-lambda @17.32-17.41 + (args + (p-assign @17.33-17.34 (ident "x"))) + (e-binop @17.36-17.41 (op "add") + (e-lookup-local @17.36-17.37 + (p-assign @17.33-17.34 (ident "x"))) + (e-int @17.40-17.41 (value "3")))) + (e-lookup-local @17.43-17.44 + (p-assign @17.18-17.19 (ident "n")))))) + (annotation @17.1-17.14 + (declared-type + (ty-fn @16.17-16.27 (effectful false) + (ty @16.17-16.20 (name "I64")) + (ty @16.24-16.27 (name "I64"))))))) +~~~ +# TYPES +~~~clojure +(inferred-types + (defs + (patt @5.1-5.10 (type "a -> Error")) + (patt @9.1-9.11 (type "Error")) + (patt @13.1-13.11 (type "_arg -> _ret, _arg2 -> _ret2")) + (patt @17.1-17.14 (type "I64 -> I64"))) + (expressions + (expr @5.13-5.26 (type "a -> Error")) + (expr @9.14-9.26 (type "Error")) + (expr @13.14-13.28 (type "_arg -> _ret, _arg2 -> _ret2")) + (expr @17.17-17.45 (type "I64 -> I64")))) +~~~ diff --git a/test/snapshots/let_polymorphism_numbers.md b/test/snapshots/let_polymorphism_numbers.md index cf65292381..3b44ce7dcb 100644 --- a/test/snapshots/let_polymorphism_numbers.md +++ b/test/snapshots/let_polymorphism_numbers.md @@ -255,7 +255,7 @@ main = |_| { (patt @13.1-13.13 (type "Num(_size)")) (patt @16.1-16.10 (type "Num(_size)")) (patt @17.1-17.15 (type "Num(_size)")) - (patt @20.1-20.7 (type "Num(_size) -> Num(_size2)")) + (patt @20.1-20.7 (type "_arg -> Num(_size)")) (patt @23.1-23.12 (type "Num(_size)")) (patt @24.1-24.14 (type "Num(_size)")) (patt @26.1-26.5 (type "_arg -> Num(_size)"))) @@ -268,7 +268,7 @@ main = |_| { (expr @13.16-13.23 (type "Num(_size)")) (expr @16.13-16.23 (type "Num(_size)")) (expr @17.18-17.27 (type "Num(_size)")) - (expr @20.10-20.19 (type "Num(_size) -> Num(_size2)")) + (expr @20.10-20.19 (type "_arg -> Num(_size)")) (expr @23.15-23.24 (type "Num(_size)")) (expr @24.17-24.28 (type "Num(_size)")) (expr @26.8-29.2 (type "_arg -> Num(_size)")))) diff --git a/test/snapshots/plume_package/Color.md b/test/snapshots/plume_package/Color.md index 03ab37fb79..bab5a5227e 100644 --- a/test/snapshots/plume_package/Color.md +++ b/test/snapshots/plume_package/Color.md @@ -80,6 +80,7 @@ is_named_color = |str|{ # EXPECTED UNUSED VARIABLE - Color.md:30:5:30:25 UNDEFINED VARIABLE - Color.md:68:14:68:27 +INVALID NOMINAL TAG - Color.md:23:5:23:33 TYPE MISMATCH - Color.md:26:7:26:46 # PROBLEMS **UNUSED VARIABLE** diff --git a/test/snapshots/records/record_with_complex_types.md b/test/snapshots/records/record_with_complex_types.md index 79ec501d83..950bbf5531 100644 --- a/test/snapshots/records/record_with_complex_types.md +++ b/test/snapshots/records/record_with_complex_types.md @@ -234,5 +234,5 @@ CloseCurly(15:1-15:2),EndOfFile(15:2-15:2), ~~~ # TYPES ~~~clojure -(expr @1.1-15.2 (type "{ name: Str, scores: List(Num(_size)), status: [Active({ since: Str })]_others, preferences: { theme: [Dark]_others2, notifications: [Email(Str)]_others3 }, metadata: Result({ tags: List(Str), permissions: List([Read]_others4) }, err), callback: Num(_size2) -> Num(_size3), nested: { items: List([Some(Str)][None]_others5), result: [Success({ data: List(Num(_size4)), timestamp: Str })]_others6 } }")) +(expr @1.1-15.2 (type "{ name: Str, scores: List(Num(_size)), status: [Active({ since: Str })]_others, preferences: { theme: [Dark]_others2, notifications: [Email(Str)]_others3 }, metadata: Result({ tags: List(Str), permissions: List([Read]_others4) }, err), callback: _arg -> Num(_size2), nested: { items: List([Some(Str)][None]_others5), result: [Success({ data: List(Num(_size3)), timestamp: Str })]_others6 } }")) ~~~ diff --git a/test/snapshots/test_exact_pattern_crash.md b/test/snapshots/test_exact_pattern_crash.md index 9b7512940c..b149c129f6 100644 --- a/test/snapshots/test_exact_pattern_crash.md +++ b/test/snapshots/test_exact_pattern_crash.md @@ -57,7 +57,7 @@ This expression is used in an unexpected way: ^^^^^^^^ It is of type: - _Num(_size), Num(_size2), Num(_size3) -> Num(_size4), Num(_size5) -> Num(_size6) -> _ret_ + _Num(_size), Num(_size2), _arg -> Num(_size3), _arg2 -> Num(_size4) -> _ret_ But you are trying to use it as: _Pair(a, b), a -> c, b -> d -> Pair(c, d)_ diff --git a/test/snapshots/unused_vars_simple.md b/test/snapshots/unused_vars_simple.md index 45d02e3dec..53614c5379 100644 --- a/test/snapshots/unused_vars_simple.md +++ b/test/snapshots/unused_vars_simple.md @@ -256,12 +256,12 @@ main! = |_| { (patt @4.1-4.15 (type "_arg -> Num(_size)")) (patt @7.1-7.16 (type "_arg -> _ret")) (patt @10.1-10.18 (type "_arg -> Num(_size)")) - (patt @13.1-13.13 (type "Num(_size) -> Num(_size2)")) + (patt @13.1-13.13 (type "_arg -> Num(_size)")) (patt @15.1-15.6 (type "_arg -> Num(_size)"))) (expressions (expr @4.18-4.24 (type "_arg -> Num(_size)")) (expr @7.19-7.34 (type "_arg -> _ret")) (expr @10.21-10.35 (type "_arg -> Num(_size)")) - (expr @13.16-13.35 (type "Num(_size) -> Num(_size2)")) + (expr @13.16-13.35 (type "_arg -> Num(_size)")) (expr @15.9-21.2 (type "_arg -> Num(_size)")))) ~~~