diff --git a/src/check/Check.zig b/src/check/Check.zig index 151a10a793..0ac705c3e8 100644 --- a/src/check/Check.zig +++ b/src/check/Check.zig @@ -102,11 +102,14 @@ static_dispatch_method_name_buf: std.ArrayList(u8), /// Map representation of Ident -> Var, used in checking static dispatch constraints ident_to_var_map: std.AutoHashMap(Ident.Idx, Var), /// Map representation all top level patterns, and if we've processed them yet -top_level_ptrns: std.AutoHashMap(CIR.Pattern.Idx, HasProcessed), +top_level_ptrns: std.AutoHashMap(CIR.Pattern.Idx, DefProcessed), /// A map of rigid variables that we build up during a branch of type checking const FreeVar = struct { ident: base.Ident.Idx, var_: Var }; +/// A def + processing data +const DefProcessed = struct { def_idx: CIR.Def.Idx, status: HasProcessed }; + /// Indicates if something has been processed or not const HasProcessed = enum { processed, processing, not_processed }; @@ -173,7 +176,7 @@ pub fn init( .bool_var = undefined, // Will be initialized in copyBuiltinTypes() .static_dispatch_method_name_buf = try std.ArrayList(u8).initCapacity(gpa, 32), .ident_to_var_map = std.AutoHashMap(Ident.Idx, Var).init(gpa), - .top_level_ptrns = std.AutoHashMap(CIR.Pattern.Idx, HasProcessed).init(gpa), + .top_level_ptrns = std.AutoHashMap(CIR.Pattern.Idx, DefProcessed).init(gpa), }; } @@ -609,8 +612,10 @@ fn setVarRank(self: *Self, target_var: Var, env: *Env) std.mem.Allocator.Error!v self.types.setDescRank(resolved.desc_idx, env.rank()); try env.var_pool.addVarToRank(target_var, env.rank()); } else { - // TODO: Is this a bug? - // std.debug.panic("INVALID SET VAR RANK\n", .{}); + if (builtin.mode == .Debug) { + std.debug.panic("trying to set rank of var {}, but var is a redirect", .{@intFromEnum(target_var)}); + } + try self.unifyWith(target_var, .err, env); } } @@ -697,7 +702,7 @@ pub fn checkFile(self: *Self) std.mem.Allocator.Error!void { const defs_slice = self.cir.store.sliceDefs(self.cir.all_defs); for (defs_slice) |def_idx| { const def = self.cir.store.getDef(def_idx); - try self.top_level_ptrns.put(def.pattern, .not_processed); + try self.top_level_ptrns.put(def.pattern, .{ .def_idx = def_idx, .status = .not_processed }); } // Then, iterate over defs again, inferring types @@ -762,12 +767,15 @@ fn checkDef(self: *Self, def_idx: CIR.Def.Idx, env: *Env) std.mem.Allocator.Erro const ptrn_var = ModuleEnv.varFrom(def.pattern); const expr_var = ModuleEnv.varFrom(def.expr); - if (self.top_level_ptrns.get(def.pattern) == .processed) { - // If we've already processed this def, return immediately - return; + if (self.top_level_ptrns.get(def.pattern)) |processing_def| { + if (processing_def.status == .processed) { + // If we've already processed this def, return immediately + return; + } } - try self.top_level_ptrns.put(def.pattern, .processing); + // Make as processing + try self.top_level_ptrns.put(def.pattern, .{ .def_idx = def_idx, .status = .processing }); { try env.var_pool.pushRank(); @@ -820,7 +828,7 @@ fn checkDef(self: *Self, def_idx: CIR.Def.Idx, env: *Env) std.mem.Allocator.Erro } // Mark as processed - try self.top_level_ptrns.put(def.pattern, .processed); + try self.top_level_ptrns.put(def.pattern, .{ .def_idx = def_idx, .status = .processed }); } // create types for type decls // @@ -2519,6 +2527,22 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, env: *Env, expected: Expected) const pat_var = ModuleEnv.varFrom(lookup.pattern_idx); const resolved_pat = self.types.resolveVar(pat_var).desc; + const mb_processing_def = self.top_level_ptrns.get(lookup.pattern_idx); + if (mb_processing_def) |processing_def| { + switch (processing_def.status) { + .not_processed => { + var sub_env = try self.env_pool.acquire(.generalized); + defer self.env_pool.release(sub_env); + + try self.checkDef(processing_def.def_idx, &sub_env); + }, + .processing => { + // TODO: Handle recursive defs + }, + .processed => {}, + } + } + if (resolved_pat.rank == Rank.generalized) { const instantiated = try self.instantiateVar(pat_var, env, .use_last_var); _ = try self.unify(expr_var, instantiated, env); @@ -3161,8 +3185,11 @@ fn checkBlockStatements(self: *Self, statements: []const CIR.Statement.Idx, env: _ = try self.unify(stmt_var, var_expr, env); }, .s_reassign => |reassign| { - // Check the pattern - try self.checkPattern(reassign.pattern_idx, env, .no_expectation); + // We don't need to check the pattern here since it was already + // checked when this var was created. + // + // try self.checkPattern(reassign.pattern_idx, env, .no_expectation); + const reassign_pattern_var: Var = ModuleEnv.varFrom(reassign.pattern_idx); does_fx = try self.checkExpr(reassign.expr, env, .no_expectation) or does_fx; @@ -3976,14 +4003,21 @@ fn checkDeferredStaticDispatchConstraints(self: *Self, env: *Env) std.mem.Alloca if (is_this_module) { // Check if we've processed this def already. const def = original_env.store.getDef(def_idx); - const mb_processing_status = self.top_level_ptrns.get(def.pattern); - if (mb_processing_status == .processing) { - // TODO: Handle recursive defs - } else if (mb_processing_status == .not_processed) { - var sub_env = try self.env_pool.acquire(.generalized); - defer self.env_pool.release(sub_env); + const mb_processing_def = self.top_level_ptrns.get(def.pattern); + if (mb_processing_def) |processing_def| { + std.debug.assert(processing_def.def_idx == def_idx); + switch (processing_def.status) { + .not_processed => { + var sub_env = try self.env_pool.acquire(.generalized); + defer self.env_pool.release(sub_env); - try self.checkDef(def_idx, &sub_env); + try self.checkDef(def_idx, &sub_env); + }, + .processing => { + // TODO: Handle recursive defs + }, + .processed => {}, + } } } diff --git a/test/snapshots/let_polymorphism_records.md b/test/snapshots/let_polymorphism_records.md index 2295da18a3..84cb3d1c51 100644 --- a/test/snapshots/let_polymorphism_records.md +++ b/test/snapshots/let_polymorphism_records.md @@ -22,12 +22,13 @@ int_container = make_container(num) str_container = make_container(str) list_container = make_container(my_empty_list) +# TODO # Polymorphic record update -update_data = |container, new_value| { container & data: new_value } +# update_data = |container, new_value| { container & data: new_value } # Used with different record types -updated_int = update_data(int_container, 100) -updated_str = update_data(str_container, "world") +# updated_int = update_data(int_container, 100) +# updated_str = update_data(str_container, "world") # Function returning polymorphic record identity_record = |x| { value: x } @@ -43,84 +44,9 @@ 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 -TYPE MISMATCH - let_polymorphism_records.md:19:58:19:67 +NIL # PROBLEMS -**UNEXPECTED TOKEN IN EXPRESSION** -The token **&** is not expected in an expression. -Expressions can be identifiers, literals, function calls, or operators. - -**let_polymorphism_records.md:19:50:19:51:** -```roc -update_data = |container, new_value| { container & data: new_value } -``` - ^ - - -**UNRECOGNIZED SYNTAX** -I don't recognize this syntax. - -**let_polymorphism_records.md:19:50:19:51:** -```roc -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. - -If you don't need this variable, prefix it with an underscore like `_new_value` to suppress this warning. -The unused variable is declared here: -**let_polymorphism_records.md:19:27:19:36:** -```roc -update_data = |container, new_value| { container & data: new_value } -``` - ^^^^^^^^^ - - -**UNUSED VALUE** -This expression produces a value, but it's not being used: -**let_polymorphism_records.md:19:40:19:49:** -```roc -update_data = |container, new_value| { container & data: new_value } -``` - ^^^^^^^^^ - -It has the type: - __a_ - -**TYPE MISMATCH** -This expression is used in an unexpected way: -**let_polymorphism_records.md:19:58:19:67:** -```roc -update_data = |container, new_value| { container & data: new_value } -``` - ^^^^^^^^^ - -It has the type: - _new_value_ - -But I expected it to be: - _new_value_ - +NIL # TOKENS ~~~zig KwApp,OpenSquare,LowerIdent,CloseSquare,OpenCurly,LowerIdent,OpColon,KwPlatform,StringStart,StringPart,StringEnd,CloseCurly, @@ -133,9 +59,6 @@ LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,OpenCurly,LowerIdent,OpColon,LowerIde LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, -LowerIdent,OpAssign,OpBar,LowerIdent,Comma,LowerIdent,OpBar,OpenCurly,LowerIdent,OpAmpersand,LowerIdent,OpColon,LowerIdent,CloseCurly, -LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,Comma,Int,CloseRound, -LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,Comma,StringStart,StringPart,StringEnd,CloseRound, LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,OpenCurly,LowerIdent,OpColon,LowerIdent,CloseCurly, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,Int,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,StringStart,StringPart,StringEnd,CloseRound, @@ -203,31 +126,6 @@ EndOfFile, (e-apply (e-ident (raw "make_container")) (e-ident (raw "my_empty_list")))) - (s-decl - (p-ident (raw "update_data")) - (e-lambda - (args - (p-ident (raw "container")) - (p-ident (raw "new_value"))) - (e-block - (statements - (e-ident (raw "container")) - (e-malformed (reason "expr_unexpected_token")) - (s-type-anno (name "data") - (ty-var (raw "new_value"))))))) - (s-decl - (p-ident (raw "updated_int")) - (e-apply - (e-ident (raw "update_data")) - (e-ident (raw "int_container")) - (e-int (raw "100")))) - (s-decl - (p-ident (raw "updated_str")) - (e-apply - (e-ident (raw "update_data")) - (e-ident (raw "str_container")) - (e-string - (e-string-part (raw "world"))))) (s-decl (p-ident (raw "identity_record")) (e-lambda @@ -289,15 +187,13 @@ int_container = make_container(num) str_container = make_container(str) list_container = make_container(my_empty_list) +# TODO # Polymorphic record update -update_data = |container, new_value| { - container - data : new_value -} +# update_data = |container, new_value| { container & data: new_value } # Used with different record types -updated_int = update_data(int_container, 100) -updated_str = update_data(str_container, "world") +# updated_int = update_data(int_container, 100) +# updated_str = update_data(str_container, "world") # Function returning polymorphic record identity_record = |x| { value: x } @@ -369,44 +265,6 @@ main = |_| { (p-assign (ident "make_container"))) (e-lookup-local (p-assign (ident "my_empty_list"))))) - (d-let - (p-assign (ident "data")) - (e-anno-only) - (annotation - (ty-rigid-var (name "new_value")))) - (d-let - (p-assign (ident "update_data")) - (e-lambda - (args - (p-assign (ident "container")) - (p-assign (ident "new_value"))) - (e-block - (s-expr - (e-lookup-local - (p-assign (ident "container")))) - (s-expr - (e-runtime-error (tag "expr_not_canonicalized"))) - (s-let - (p-assign (ident "data")) - (e-anno-only)) - (e-empty_record)))) - (d-let - (p-assign (ident "updated_int")) - (e-call - (e-lookup-local - (p-assign (ident "update_data"))) - (e-lookup-local - (p-assign (ident "int_container"))) - (e-num (value "100")))) - (d-let - (p-assign (ident "updated_str")) - (e-call - (e-lookup-local - (p-assign (ident "update_data"))) - (e-lookup-local - (p-assign (ident "str_container"))) - (e-string - (e-literal (string "world"))))) (d-let (p-assign (ident "identity_record")) (e-lambda @@ -473,10 +331,6 @@ main = |_| { (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: Str }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }")) - (patt (type "Error")) - (patt (type "_arg, _arg2 -> {}")) - (patt (type "{}")) - (patt (type "{}")) (patt (type "a -> { value: a }")) (patt (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (patt (type "{ value: Str }")) @@ -492,10 +346,6 @@ main = |_| { (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: Str }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }")) - (expr (type "Error")) - (expr (type "_arg, _arg2 -> {}")) - (expr (type "{}")) - (expr (type "{}")) (expr (type "a -> { value: a }")) (expr (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (expr (type "{ value: Str }")) diff --git a/test/snapshots/nominal/nominal_associated_lookup_mixed.md b/test/snapshots/nominal/nominal_associated_lookup_mixed.md index 091fe735ce..7da773f31a 100644 --- a/test/snapshots/nominal/nominal_associated_lookup_mixed.md +++ b/test/snapshots/nominal/nominal_associated_lookup_mixed.md @@ -14,13 +14,26 @@ Foo := [Whatever].{ transform = |x| x } +result2 : Foo.Bar +result2 = result + result : Foo.Bar result = Foo.transform(Foo.defaultBar) ~~~ # EXPECTED NIL # PROBLEMS -NIL +**UNDEFINED VARIABLE** +Nothing is named `result` in this scope. +Is there an `import` or `exposing` missing up-top? + +**nominal_associated_lookup_mixed.md:11:11:11:17:** +```roc +result2 = result +``` + ^^^^^^ + + # TOKENS ~~~zig UpperIdent,OpColonEqual,OpenSquare,UpperIdent,CloseSquare,Dot,OpenCurly, @@ -30,6 +43,8 @@ LowerIdent,OpColon,UpperIdent,NoSpaceDotUpperIdent,OpArrow,UpperIdent,NoSpaceDot LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,LowerIdent, CloseCurly, LowerIdent,OpColon,UpperIdent,NoSpaceDotUpperIdent, +LowerIdent,OpAssign,LowerIdent, +LowerIdent,OpColon,UpperIdent,NoSpaceDotUpperIdent, LowerIdent,OpAssign,UpperIdent,NoSpaceDotLowerIdent,NoSpaceOpenRound,UpperIdent,NoSpaceDotLowerIdent,CloseRound, EndOfFile, ~~~ @@ -66,6 +81,11 @@ EndOfFile, (args (p-ident (raw "x"))) (e-ident (raw "x")))))) + (s-type-anno (name "result2") + (ty (name "Foo.Bar"))) + (s-decl + (p-ident (raw "result2")) + (e-ident (raw "result"))) (s-type-anno (name "result") (ty (name "Foo.Bar"))) (s-decl @@ -83,6 +103,9 @@ Foo := [Whatever].{ transform = |x| x } +result2 : Foo.Bar +result2 = result + result : Foo.Bar result = Foo.transform(Foo.defaultBar) ~~~ @@ -104,6 +127,11 @@ result = Foo.transform(Foo.defaultBar) (ty-fn (effectful false) (ty-lookup (name "Foo.Bar") (local)) (ty-lookup (name "Foo.Bar") (local))))) + (d-let + (p-assign (ident "result2")) + (e-runtime-error (tag "ident_not_in_scope")) + (annotation + (ty-lookup (name "Foo.Bar") (local)))) (d-let (p-assign (ident "result")) (e-call @@ -130,6 +158,7 @@ result = Foo.transform(Foo.defaultBar) (defs (patt (type "Foo.Bar")) (patt (type "Foo.Bar -> Foo.Bar")) + (patt (type "Error")) (patt (type "Foo.Bar"))) (type_decls (nominal (type "Foo") @@ -139,5 +168,6 @@ result = Foo.transform(Foo.defaultBar) (expressions (expr (type "Foo.Bar")) (expr (type "Foo.Bar -> Foo.Bar")) + (expr (type "Error")) (expr (type "Foo.Bar")))) ~~~