@@ -251,7 +251,7 @@ pub fn populateModuleEnvs(
251251 module_envs_map : * std .AutoHashMap (Ident.Idx , AutoImportedType ),
252252 calling_module_env : * ModuleEnv ,
253253 builtin_module_env : * const ModuleEnv ,
254- builtin_indices : anytype , // Has fields: bool_type, try_type, dict_type, set_type, str_type, and numeric types
254+ builtin_indices : CIR.BuiltinIndices , // Has fields: bool_type, try_type, dict_type, set_type, str_type, and numeric types
255255) ! void {
256256 const types_to_add = .{
257257 .{ "Bool" , builtin_indices .bool_type },
@@ -369,7 +369,7 @@ const Self = @This();
369369/// If parent_name is provided, creates a qualified name (e.g., "Foo.Bar")
370370fn processTypeDeclFirstPass (
371371 self : * Self ,
372- type_decl : anytype ,
372+ type_decl : std . meta . fieldInfo ( AST.Statement , .type_decl ). type ,
373373 parent_name : ? Ident.Idx ,
374374 defer_associated_blocks : bool ,
375375) std .mem .Allocator .Error ! void {
@@ -924,6 +924,7 @@ fn processAssociatedItemsSecondPass(
924924 .decl = > | decl | {
925925 // Canonicalize the declaration with qualified name
926926 const pattern = self .parse_ir .store .getPattern (decl .pattern );
927+ const pattern_region = self .parse_ir .tokenizedRegionToRegion (pattern .to_tokenized_region ());
927928 if (pattern == .ident ) {
928929 const pattern_ident_tok = pattern .ident .ident_tok ;
929930 if (self .parse_ir .tokens .resolveIdentifier (pattern_ident_tok )) | decl_ident | {
@@ -942,12 +943,11 @@ fn processAssociatedItemsSecondPass(
942943 }
943944 } else {
944945 // Non-identifier patterns are not supported in associated blocks
945- const region = self .parse_ir .tokenizedRegionToRegion (decl .region );
946946 const feature = try self .env .insertString ("non-identifier patterns in associated blocks" );
947947 try self .env .pushDiagnostic (Diagnostic {
948948 .not_implemented = .{
949949 .feature = feature ,
950- .region = region ,
950+ .region = pattern_region ,
951951 },
952952 });
953953 }
@@ -1100,19 +1100,19 @@ fn processAssociatedItemsFirstPass(
11001100 // processAssociatedItemsSecondPass will later use updatePlaceholder to replace these
11011101 const pattern = self .parse_ir .store .getPattern (decl .pattern );
11021102 if (pattern == .ident ) {
1103+ const pattern_region = self .parse_ir .tokenizedRegionToRegion (pattern .to_tokenized_region ());
11031104 const pattern_ident_tok = pattern .ident .ident_tok ;
11041105 if (self .parse_ir .tokens .resolveIdentifier (pattern_ident_tok )) | decl_ident | {
11051106 // Build qualified name (e.g., "Foo.Bar.baz")
11061107 const qualified_idx = try self .env .insertQualifiedIdent (self .env .getIdent (parent_name ), self .env .getIdent (decl_ident ));
11071108
11081109 // Create placeholder pattern with qualified name
1109- const region = self .parse_ir .tokenizedRegionToRegion (decl .region );
11101110 const placeholder_pattern = Pattern {
11111111 .assign = .{
11121112 .ident = qualified_idx ,
11131113 },
11141114 };
1115- const placeholder_pattern_idx = try self .env .addPattern (placeholder_pattern , region );
1115+ const placeholder_pattern_idx = try self .env .addPattern (placeholder_pattern , pattern_region );
11161116
11171117 // Also compute type-qualified name (e.g., "List.map")
11181118 // Re-fetch identifiers since insertQualifiedIdent may have reallocated the identifier table
@@ -1274,6 +1274,40 @@ pub fn canonicalizeFile(
12741274 try self .processTypeDeclFirstPass (type_decl , null , true ); // defer associated blocks
12751275 }
12761276 },
1277+ .decl = > | decl | {
1278+ // Introduce declarations for forawrd/recursive references
1279+ const pattern = self .parse_ir .store .getPattern (decl .pattern );
1280+ if (pattern == .ident ) {
1281+ const pattern_region = self .parse_ir .tokenizedRegionToRegion (pattern .to_tokenized_region ());
1282+ const pattern_ident_tok = pattern .ident .ident_tok ;
1283+ if (self .parse_ir .tokens .resolveIdentifier (pattern_ident_tok )) | decl_ident | {
1284+ // Create placeholder pattern with qualified name
1285+ const placeholder_pattern = Pattern {
1286+ .assign = .{ .ident = decl_ident },
1287+ };
1288+ const placeholder_pattern_idx = try self .env .addPattern (placeholder_pattern , pattern_region );
1289+
1290+ // Introduce the qualified name to scope
1291+ switch (try self .scopeIntroduceInternal (self .env .gpa , .ident , decl_ident , placeholder_pattern_idx , false , true )) {
1292+ .success = > {},
1293+ .shadowing_warning = > | shadowed_pattern_idx | {
1294+ const original_region = self .env .store .getPatternRegion (shadowed_pattern_idx );
1295+ try self .env .pushDiagnostic (Diagnostic { .shadowing_warning = .{
1296+ .ident = decl_ident ,
1297+ .region = pattern_region ,
1298+ .original_region = original_region ,
1299+ } });
1300+ },
1301+ .top_level_var_error = > {
1302+ // This shouldn't happen for declarations in associated blocks
1303+ },
1304+ .var_across_function_boundary = > {
1305+ // This shouldn't happen for declarations in associated blocks
1306+ },
1307+ }
1308+ }
1309+ }
1310+ },
12771311 else = > {
12781312 // Skip non-type-declaration statements in first pass
12791313 },
@@ -2872,23 +2906,29 @@ fn canonicalizeDeclWithAnnotation(
28722906 const trace = tracy .trace (@src ());
28732907 defer trace .end ();
28742908
2875- const pattern_idx = try self .canonicalizePatternOrMalformed (decl .pattern );
2909+ // Either find the placeholder pattern insert in the first past if ident,
2910+ // otherwise canonicalize the pattern
2911+ const pattern = self .parse_ir .store .getPattern (decl .pattern );
2912+ const pattern_idx = blk : {
2913+ if (pattern == .ident ) {
2914+ const pattern_ident_tok = pattern .ident .ident_tok ;
2915+ if (self .parse_ir .tokens .resolveIdentifier (pattern_ident_tok )) | decl_ident | {
2916+ // Look up the placeholder pattern that was created in the first pass
2917+ const lookup_result = self .scopeLookup (.ident , decl_ident );
2918+ switch (lookup_result ) {
2919+ .found = > | pattern_idx | break :blk pattern_idx ,
2920+ .not_found = > unreachable , // Pattern should have been created in first pass
2921+ }
2922+ } else {
2923+ break :blk try self .canonicalizePatternOrMalformed (decl .pattern );
2924+ }
2925+ } else {
2926+ break :blk try self .canonicalizePatternOrMalformed (decl .pattern );
2927+ }
2928+ };
2929+
28762930 const can_expr = try self .canonicalizeExprOrMalformed (decl .body );
28772931
2878- // Create the def entry and set def type variable to a flex var
2879- //
2880- // We always use a flex variable for the definition, regardless of whether there's
2881- // an annotation. This is because:
2882- // 1. If there's no annotation, we need a flex var for normal type inference
2883- // 2. If there IS an annotation, we still use a flex var to avoid copying the
2884- // annotation's type content. This is necessary because if the annotation contains
2885- // an alias (e.g., `empty : ConsList(a)`), that alias expects its type arguments
2886- // to live at specific memory offsets relative to the alias's own type variable.
2887- // Copying the alias content to a different type variable would break this assumption.
2888- // 3. During type checking, the definition's flex var will be unified with the
2889- // annotation's type (if present) or with the inferred type from the expression
2890- // 4. Type errors will be caught during unification if the implementation doesn't
2891- // match the annotation
28922932 const region = self .parse_ir .tokenizedRegionToRegion (decl .region );
28932933 const def_idx = self .env .addDef (.{
28942934 .pattern = pattern_idx ,
@@ -2957,7 +2997,7 @@ fn parseSingleQuoteCodepoint(
29572997
29582998fn canonicalizeStringLike (
29592999 self : * Self ,
2960- e : anytype ,
3000+ e : AST.Expr.StringLike ,
29613001 is_multiline : bool ,
29623002) std.mem.Allocator.Error ! CanonicalizedExpr {
29633003 // Get all the string parts
@@ -8346,6 +8386,9 @@ fn checkScopeForUnusedVariables(self: *Self, scope: *const Scope) std.mem.Alloca
83468386
83478387 // Report unused variables in sorted order
83488388 for (unused_vars .items ) | unused | {
8389+ // TODO: Currently, static dispatch functions are marked as "unused"
8390+ // even if they are used. As a tmp workaround, this is commented out
8391+
83498392 try self .env .pushDiagnostic (Diagnostic { .unused_variable = .{
83508393 .ident = unused .ident ,
83518394 .region = unused .region ,
@@ -9372,7 +9415,7 @@ fn findMatchingTypeIdent(self: *Self) ?Ident.Idx {
93729415
93739416/// Expose all associated items of a type declaration (recursively for nested types)
93749417/// This is used for type modules where all associated items are implicitly exposed
9375- fn exposeAssociatedItems (self : * Self , parent_name : Ident.Idx , type_decl : anytype ) std.mem.Allocator.Error ! void {
9418+ fn exposeAssociatedItems (self : * Self , parent_name : Ident.Idx , type_decl : std . meta . fieldInfo ( AST.Statement , .type_decl ). type ) std .mem .Allocator .Error ! void {
93769419 if (type_decl .associated ) | assoc | {
93779420 for (self .parse_ir .store .statementSlice (assoc .statements )) | assoc_stmt_idx | {
93789421 const assoc_stmt = self .parse_ir .store .getStatement (assoc_stmt_idx );
0 commit comments