From ba3e667e5b3056a1bfc777958737f6ceff835dce Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 8 Nov 2025 18:13:28 -0500 Subject: [PATCH] Fix Ubuntu CI --- src/canonicalize/Can.zig | 137 +++++++++++++----- .../multiline_without_comma/everything.md | 22 +-- test/snapshots/fuzz_crash/fuzz_crash_042.md | 22 +-- .../pass/underscore_in_regular_annotations.md | 27 ++++ 4 files changed, 148 insertions(+), 60 deletions(-) diff --git a/src/canonicalize/Can.zig b/src/canonicalize/Can.zig index 701d1b7e56..6fc2c02515 100644 --- a/src/canonicalize/Can.zig +++ b/src/canonicalize/Can.zig @@ -1280,51 +1280,77 @@ pub fn canonicalizeFile( } } - // Phase 1.5.5: Create placeholders for top-level decls - // This ensures they're available when processing associated blocks - // IMPORTANT: Only do this for type-modules, not apps/hosted/etc - // Apps and other module types process their decls normally without placeholders + // Phase 1.5.5: Process anno-only top-level type annotations EARLY + // For type-modules, anno-only top-level type annotations (like list_get_unsafe) need to be + // processed before associated blocks so they can be referenced inside those blocks + // IMPORTANT: Only process anno-only (no matching decl), and only for type-modules switch (self.env.module_kind) { .type_module => { const top_level_stmts = self.parse_ir.store.statementSlice(file.statements); - for (top_level_stmts) |stmt_id| { + var i: usize = 0; + while (i < top_level_stmts.len) : (i += 1) { + const stmt_id = top_level_stmts[i]; const stmt = self.parse_ir.store.getStatement(stmt_id); - switch (stmt) { - .decl => |decl| { - const pattern = self.parse_ir.store.getPattern(decl.pattern); - if (pattern == .ident) { - const pattern_ident_tok = pattern.ident.ident_tok; - if (self.parse_ir.tokens.resolveIdentifier(pattern_ident_tok)) |decl_ident| { - const region = self.parse_ir.tokenizedRegionToRegion(decl.region); - const placeholder_pattern = Pattern{ - .assign = .{ - .ident = decl_ident, - }, - }; - const placeholder_pattern_idx = try self.env.addPattern(placeholder_pattern, region); - try self.placeholder_idents.put(self.env.gpa, decl_ident, {}); - const current_scope = &self.scopes.items[self.scopes.items.len - 1]; - try current_scope.idents.put(self.env.gpa, decl_ident, placeholder_pattern_idx); + if (stmt == .type_anno) { + const ta = stmt.type_anno; + const name_ident = self.parse_ir.tokens.resolveIdentifier(ta.name) orelse continue; + + // Check if there's a matching decl (skipping malformed statements) + const has_matching_decl = blk: { + var next_i = i + 1; + while (next_i < top_level_stmts.len) : (next_i += 1) { + const next_stmt = self.parse_ir.store.getStatement(top_level_stmts[next_i]); + // Skip malformed statements + if (next_stmt == .malformed) continue; + // Check if this is a matching decl + if (next_stmt == .decl) { + const next_pattern = self.parse_ir.store.getPattern(next_stmt.decl.pattern); + if (next_pattern == .ident) { + if (self.parse_ir.tokens.resolveIdentifier(next_pattern.ident.ident_tok)) |decl_ident| { + break :blk name_ident.idx == decl_ident.idx; + } + } } + // Found a non-malformed, non-matching statement + break :blk false; } - }, - .type_anno => |type_anno| { - // Also create placeholders for top-level type annotations (like list_get_unsafe) - // These are anno-only defs that need to be available in associated blocks - if (self.parse_ir.tokens.resolveIdentifier(type_anno.name)) |anno_ident| { - const region = self.parse_ir.tokenizedRegionToRegion(type_anno.region); - const placeholder_pattern = Pattern{ - .assign = .{ - .ident = anno_ident, - }, - }; - const placeholder_pattern_idx = try self.env.addPattern(placeholder_pattern, region); - try self.placeholder_idents.put(self.env.gpa, anno_ident, {}); - const current_scope = &self.scopes.items[self.scopes.items.len - 1]; - try current_scope.idents.put(self.env.gpa, anno_ident, placeholder_pattern_idx); + // Reached end of statements + break :blk false; + }; + + // Skip if there's a matching decl - it will be processed normally + if (has_matching_decl) continue; + + const region = self.parse_ir.tokenizedRegionToRegion(ta.region); + + // Extract type variables and canonicalize the annotation + const type_vars_top: u32 = @intCast(self.scratch_idents.top()); + try self.extractTypeVarIdentsFromASTAnno(ta.anno, type_vars_top); + const type_var_scope = self.scopeEnterTypeVar(); + defer self.scopeExitTypeVar(type_var_scope); + const type_anno_idx = try self.canonicalizeTypeAnno(ta.anno, .inline_anno); + + // Canonicalize where clauses if present + const where_clauses = if (ta.where) |where_coll| blk: { + const where_slice = self.parse_ir.store.whereClauseSlice(.{ .span = self.parse_ir.store.getCollection(where_coll).span }); + const where_start = self.env.store.scratchWhereClauseTop(); + for (where_slice) |where_idx| { + const canonicalized_where = try self.canonicalizeWhereClause(where_idx, .inline_anno); + try self.env.store.addScratchWhereClause(canonicalized_where); } - }, - else => {}, + break :blk try self.env.store.whereClauseSpanFrom(where_start); + } else null; + + // Create the anno-only def immediately + const def_idx = try self.createAnnoOnlyDef(name_ident, type_anno_idx, where_clauses, region); + try self.env.store.addScratchDef(def_idx); + + // If exposed, register it + const ident_text = self.env.getIdent(name_ident); + if (self.exposed_ident_texts.contains(ident_text)) { + const def_idx_u16: u16 = @intCast(@intFromEnum(def_idx)); + try self.env.setExposedNodeIndexById(name_ident, def_idx_u16); + } } } }, @@ -1485,6 +1511,41 @@ pub fn canonicalizeFile( continue; }; + // For type-modules, check if this is an anno-only annotation that was already processed in Phase 1.5.5 + // We need to check if there's a matching decl - if there isn't, this was processed early + switch (self.env.module_kind) { + .type_module => { + // Check if there's a matching decl (skipping malformed statements) + const has_matching_decl = blk: { + var check_i = i + 1; + while (check_i < ast_stmt_idxs.len) : (check_i += 1) { + const check_stmt = self.parse_ir.store.getStatement(ast_stmt_idxs[check_i]); + // Skip malformed statements + if (check_stmt == .malformed) continue; + // Check if this is a matching decl + if (check_stmt == .decl) { + const check_pattern = self.parse_ir.store.getPattern(check_stmt.decl.pattern); + if (check_pattern == .ident) { + if (self.parse_ir.tokens.resolveIdentifier(check_pattern.ident.ident_tok)) |decl_ident| { + break :blk name_ident.idx == decl_ident.idx; + } + } + } + // Found a non-malformed, non-matching statement + break :blk false; + } + // Reached end of statements + break :blk false; + }; + + // Skip if this is anno-only (no matching decl) - it was processed in Phase 1.5.5 + if (!has_matching_decl) { + continue; + } + }, + else => {}, + } + // First, make the top of our scratch list const type_vars_top: u32 = @intCast(self.scratch_idents.top()); diff --git a/test/snapshots/formatting/multiline_without_comma/everything.md b/test/snapshots/formatting/multiline_without_comma/everything.md index d4209d51d0..49bdfa5597 100644 --- a/test/snapshots/formatting/multiline_without_comma/everything.md +++ b/test/snapshots/formatting/multiline_without_comma/everything.md @@ -1174,6 +1174,17 @@ g : e -> e where module(e).A, module(e).B ^^ +**MALFORMED WHERE CLAUSE** +This where clause could not be parsed correctly. + +**everything.md:56:12:56:17:** +```roc +g : e -> e where module(e).A, module(e).B +``` + ^^^^^ + +Check the syntax of your where clause. + **WHERE CLAUSE NOT ALLOWED IN TYPE DECLARATION** You cannot define a `where` clause inside a type declaration. @@ -1222,17 +1233,6 @@ import I2 exposing [ ``` -**MALFORMED WHERE CLAUSE** -This where clause could not be parsed correctly. - -**everything.md:56:12:56:17:** -```roc -g : e -> e where module(e).A, module(e).B -``` - ^^^^^ - -Check the syntax of your where clause. - **UNUSED VARIABLE** Variable `b` is not used anywhere in your code. diff --git a/test/snapshots/fuzz_crash/fuzz_crash_042.md b/test/snapshots/fuzz_crash/fuzz_crash_042.md index 49c7597570..6a40737f32 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_042.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_042.md @@ -23,17 +23,6 @@ import u.R}g:r->R.a.E ^ -**MODULE NOT FOUND** -The module `u.R` was not found in this Roc project. - -You're attempting to use this module here: -**fuzz_crash_042.md:1:1:1:11:** -```roc -import u.R}g:r->R.a.E -``` -^^^^^^^^^^ - - **MODULE NOT IMPORTED** There is no module with the name `R.a` imported into this Roc file. @@ -45,6 +34,17 @@ import u.R}g:r->R.a.E ^^^^^ +**MODULE NOT FOUND** +The module `u.R` was not found in this Roc project. + +You're attempting to use this module here: +**fuzz_crash_042.md:1:1:1:11:** +```roc +import u.R}g:r->R.a.E +``` +^^^^^^^^^^ + + # TOKENS ~~~zig KwImport,LowerIdent,NoSpaceDotUpperIdent,CloseCurly,LowerIdent,OpColon,LowerIdent,OpArrow,UpperIdent,NoSpaceDotLowerIdent,NoSpaceDotUpperIdent, diff --git a/test/snapshots/pass/underscore_in_regular_annotations.md b/test/snapshots/pass/underscore_in_regular_annotations.md index 31f635ea1e..424066712d 100644 --- a/test/snapshots/pass/underscore_in_regular_annotations.md +++ b/test/snapshots/pass/underscore_in_regular_annotations.md @@ -77,6 +77,24 @@ process = |list| "processed" ^^^^ +**DUPLICATE DEFINITION** +The name `transform` is being redeclared in this scope. + +The redeclaration is here: +**underscore_in_regular_annotations.md:29:1:29:10:** +```roc +transform = |_, b| b +``` +^^^^^^^^^ + +But `transform` was already defined here: +**underscore_in_regular_annotations.md:28:1:28:21:** +```roc +transform : _a -> _b -> _b +``` +^^^^^^^^^^^^^^^^^^^^ + + # TOKENS ~~~zig LowerIdent,OpColon,Underscore,OpArrow,Underscore, @@ -245,6 +263,13 @@ transform = |_, b| b # CANONICALIZE ~~~clojure (can-ir + (d-let + (p-assign (ident "transform")) + (e-anno-only) + (annotation + (ty-fn (effectful false) + (ty-rigid-var (name "_a")) + (ty-rigid-var (name "_b"))))) (d-let (p-assign (ident "main")) (e-lambda @@ -360,6 +385,7 @@ transform = |_, b| b ~~~clojure (inferred-types (defs + (patt (type "_a -> _b")) (patt (type "_arg -> _ret")) (patt (type "a -> a")) (patt (type "List(_elem) -> Str")) @@ -368,6 +394,7 @@ transform = |_, b| b (patt (type "a -> b, List(a) -> List(b)")) (patt (type "_arg, c -> c"))) (expressions + (expr (type "_a -> _b")) (expr (type "_arg -> _ret")) (expr (type "a -> a")) (expr (type "List(_elem) -> Str"))