From 02f4c3379ca78b5f2f0f349efece33436878393f Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 24 Nov 2025 22:58:00 -0500 Subject: [PATCH] Fix reallocation bug --- src/canonicalize/Can.zig | 52 ++++++++++++------- .../associated_items_truly_comprehensive.md | 21 ++------ 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/canonicalize/Can.zig b/src/canonicalize/Can.zig index 7c0b887478..a69484062a 100644 --- a/src/canonicalize/Can.zig +++ b/src/canonicalize/Can.zig @@ -1498,15 +1498,26 @@ fn processAssociatedItemsFirstPass( // Also add user-facing alias (without module prefix) so "One.Two.value" works // in addition to "module.One.Two.value" + // Add to module scope (index 0) so it's accessible from anywhere in the module const fully_qualified_text = self.env.getIdent(qualified_idx); const module_prefix = self.env.module_name; if (std.mem.startsWith(u8, fully_qualified_text, module_prefix) and fully_qualified_text.len > module_prefix.len and fully_qualified_text[module_prefix.len] == '.') { - const user_facing_text = fully_qualified_text[module_prefix.len + 1 ..]; - const user_facing_idx = try self.env.insertIdent(base.Ident.for_text(user_facing_text)); - try current_scope.idents.put(self.env.gpa, user_facing_idx, placeholder_pattern_idx); + // Copy user-facing text to a local buffer before calling insertIdent, + // because insertIdent may reallocate the string interner and invalidate + // any slices pointing into it (like fully_qualified_text) + const user_facing_start = module_prefix.len + 1; + const user_facing_len = fully_qualified_text.len - user_facing_start; + var user_facing_buf: [512]u8 = undefined; + if (user_facing_len <= user_facing_buf.len) { + @memcpy(user_facing_buf[0..user_facing_len], fully_qualified_text[user_facing_start..]); + const user_facing_text = user_facing_buf[0..user_facing_len]; + const user_facing_idx = try self.env.insertIdent(base.Ident.for_text(user_facing_text)); + // Add to module scope so it persists after associated block scope exits + try self.scopes.items[0].idents.put(self.env.gpa, user_facing_idx, placeholder_pattern_idx); + } } } } @@ -1535,15 +1546,26 @@ fn processAssociatedItemsFirstPass( try current_scope.idents.put(self.env.gpa, qualified_idx, placeholder_pattern_idx); // Also add user-facing alias (without module prefix) + // Add to module scope (index 0) so it's accessible from anywhere in the module const fully_qualified_text = self.env.getIdent(qualified_idx); const module_prefix = self.env.module_name; if (std.mem.startsWith(u8, fully_qualified_text, module_prefix) and fully_qualified_text.len > module_prefix.len and fully_qualified_text[module_prefix.len] == '.') { - const user_facing_text = fully_qualified_text[module_prefix.len + 1 ..]; - const user_facing_idx = try self.env.insertIdent(base.Ident.for_text(user_facing_text)); - try current_scope.idents.put(self.env.gpa, user_facing_idx, placeholder_pattern_idx); + // Copy user-facing text to a local buffer before calling insertIdent, + // because insertIdent may reallocate the string interner and invalidate + // any slices pointing into it (like fully_qualified_text) + const user_facing_start = module_prefix.len + 1; + const user_facing_len = fully_qualified_text.len - user_facing_start; + var user_facing_buf: [512]u8 = undefined; + if (user_facing_len <= user_facing_buf.len) { + @memcpy(user_facing_buf[0..user_facing_len], fully_qualified_text[user_facing_start..]); + const user_facing_text = user_facing_buf[0..user_facing_len]; + const user_facing_idx = try self.env.insertIdent(base.Ident.for_text(user_facing_text)); + // Add to module scope so it persists after associated block scope exits + try self.scopes.items[0].idents.put(self.env.gpa, user_facing_idx, placeholder_pattern_idx); + } } } }, @@ -8724,11 +8746,8 @@ fn scopeContains( const scope = &self.scopes.items[scope_idx]; const map = scope.itemsConst(item_kind); - var iter = map.iterator(); - while (iter.next()) |entry| { - if (name.idx == entry.key_ptr.idx) { - return entry.value_ptr.*; - } + if (map.get(name)) |pattern_idx| { + return pattern_idx; } } return null; @@ -8938,15 +8957,10 @@ pub fn scopeIntroduceInternal( const scope = &self.scopes.items[scope_idx]; const map = scope.itemsConst(item_kind); - var iter = map.iterator(); - while (iter.next()) |entry| { - if (ident_idx.idx == entry.key_ptr.idx) { - declaration_scope_idx = scope_idx; - break; - } + if (map.get(ident_idx) != null) { + declaration_scope_idx = scope_idx; + break; } - - if (declaration_scope_idx != null) break; } // Now check if there are function boundaries between declaration and current scope diff --git a/test/snapshots/nominal/associated_items_truly_comprehensive.md b/test/snapshots/nominal/associated_items_truly_comprehensive.md index a0930c4398..ff45063699 100644 --- a/test/snapshots/nominal/associated_items_truly_comprehensive.md +++ b/test/snapshots/nominal/associated_items_truly_comprehensive.md @@ -483,7 +483,6 @@ UNDEFINED VARIABLE - associated_items_truly_comprehensive.md:382:20:382:24 UNUSED VARIABLE - associated_items_truly_comprehensive.md:382:20:382:24 UNDEFINED VARIABLE - associated_items_truly_comprehensive.md:388:12:388:16 UNUSED VARIABLE - associated_items_truly_comprehensive.md:388:12:388:16 -DOES NOT EXIST - associated_items_truly_comprehensive.md:451:11:451:32 # PROBLEMS **UNDEFINED VARIABLE** Nothing is named `val4` in this scope. @@ -531,19 +530,6 @@ The unused variable is declared here: ^^^^ -**DOES NOT EXIST** -`Shadowing.useL2` does not exist. - -`Shadowing` is in scope, but it has no associated `useL2`. - -It's referenced here: -**associated_items_truly_comprehensive.md:451:11:451:32:** -```roc -shadow7 = Shadowing.L2.L3.useL2 # 2 -``` - ^^^^^^^^^^^^^^^^^^^^^ - - # TOKENS ~~~zig UpperIdent,OpColonEqual,OpenSquare,UpperIdent,CloseSquare,Dot,OpenCurly, @@ -3186,7 +3172,8 @@ anno2 = Annotated.L2.alsoTyped # 889 (p-assign (ident "associated_items_truly_comprehensive.Shadowing.L2.L3.useL1")))) (d-let (p-assign (ident "shadow7")) - (e-runtime-error (tag "nested_value_not_found"))) + (e-lookup-local + (p-assign (ident "associated_items_truly_comprehensive.Shadowing.L2.L3.useL2")))) (d-let (p-assign (ident "shadow8")) (e-lookup-local @@ -3701,7 +3688,7 @@ anno2 = Annotated.L2.alsoTyped # 889 (patt (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (patt (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (patt (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) - (patt (type "Error")) + (patt (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (patt (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (patt (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (patt (type "U64")) @@ -4035,7 +4022,7 @@ anno2 = Annotated.L2.alsoTyped # 889 (expr (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (expr (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (expr (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) - (expr (type "Error")) + (expr (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (expr (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (expr (type "e where [e.from_numeral : Numeral -> Try(e, [InvalidNumeral(Str)])]")) (expr (type "U64"))