mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge pull request #8711 from roc-lang/fix-issue-8708
Fix TypeContainedMismatch when using type alias inside Try
This commit is contained in:
commit
a14665e377
2 changed files with 94 additions and 2 deletions
|
|
@ -1036,8 +1036,15 @@ pub const Store = struct {
|
|||
var layout: Layout = undefined;
|
||||
|
||||
if (!skip_layout_computation) {
|
||||
// Mark this var as in-progress before processing
|
||||
try self.work.in_progress_vars.put(current.var_, {});
|
||||
// Mark this var as in-progress before processing.
|
||||
// Note: We don't add aliases to in_progress_vars because aliases are transparent
|
||||
// wrappers that just continue to their backing type. The alias handling code
|
||||
// does `current = backing; continue;` without ever completing the alias entry,
|
||||
// which would cause spurious cycle detection when the alias var is encountered
|
||||
// again. See issue #8708.
|
||||
if (current.desc.content != .alias) {
|
||||
try self.work.in_progress_vars.put(current.var_, {});
|
||||
}
|
||||
|
||||
layout = switch (current.desc.content) {
|
||||
.structure => |flat_type| flat_type: switch (flat_type) {
|
||||
|
|
|
|||
|
|
@ -476,3 +476,88 @@ test "addTypeVar - flex var with method constraint returning open tag union" {
|
|||
// Try should be a tag_union
|
||||
try testing.expect(try_result_layout.tag == .tag_union);
|
||||
}
|
||||
|
||||
test "addTypeVar - type alias inside Try nominal (issue #8708)" {
|
||||
// Regression test for issue #8708:
|
||||
// Using a type alias as a type argument to Try caused TypeContainedMismatch error.
|
||||
//
|
||||
// The bug was that aliases were added to in_progress_vars during layout computation
|
||||
// but never removed (because alias handling just continues to the backing type).
|
||||
// This caused spurious cycle detection when the alias was encountered again.
|
||||
//
|
||||
// Example Roc code that triggered the bug:
|
||||
// TokenContents : [EndOfFileToken]
|
||||
// get_val : {} -> Try(TokenContents, Str)
|
||||
|
||||
var lt: LayoutTest = undefined;
|
||||
lt.gpa = testing.allocator;
|
||||
lt.module_env = try ModuleEnv.init(lt.gpa, "");
|
||||
lt.type_store = try types_store.Store.init(lt.gpa);
|
||||
|
||||
// Setup identifiers
|
||||
const try_ident_idx = try lt.module_env.insertIdent(Ident.for_text("Try"));
|
||||
const token_contents_ident_idx = try lt.module_env.insertIdent(Ident.for_text("TokenContents"));
|
||||
const builtin_module_idx = try lt.module_env.insertIdent(Ident.for_text("Builtin"));
|
||||
lt.module_env.idents.builtin_module = builtin_module_idx;
|
||||
|
||||
lt.layout_store = try Store.init(<.module_env, <.type_store, null);
|
||||
lt.type_scope = TypeScope.init(lt.gpa);
|
||||
defer lt.deinit();
|
||||
|
||||
// Create the underlying tag union: [EndOfFileToken]
|
||||
const end_of_file_token_tag = types.Tag{
|
||||
.name = try lt.module_env.insertIdent(Ident.for_text("EndOfFileToken")),
|
||||
.args = try lt.type_store.appendVars(&[_]types.Var{}),
|
||||
};
|
||||
const token_tags_range = try lt.type_store.appendTags(&[_]types.Tag{end_of_file_token_tag});
|
||||
const token_tag_union = types.TagUnion{
|
||||
.tags = token_tags_range,
|
||||
.ext = try lt.type_store.freshFromContent(.{ .structure = .empty_tag_union }),
|
||||
};
|
||||
const token_tag_union_var = try lt.type_store.freshFromContent(.{ .structure = .{ .tag_union = token_tag_union } });
|
||||
|
||||
// Create the alias: TokenContents : [EndOfFileToken]
|
||||
const alias_content = try lt.type_store.mkAlias(
|
||||
.{ .ident_idx = token_contents_ident_idx },
|
||||
token_tag_union_var,
|
||||
&[_]types.Var{},
|
||||
);
|
||||
const token_contents_alias_var = try lt.type_store.freshFromContent(alias_content);
|
||||
|
||||
// Create an error type (Str is common for errors)
|
||||
const str_var = try lt.type_store.freshFromContent(.{ .structure = .empty_record }); // simplified
|
||||
|
||||
// Create Try backing: [Ok(TokenContents), Err(Str)]
|
||||
const ok_tag = types.Tag{
|
||||
.name = try lt.module_env.insertIdent(Ident.for_text("Ok")),
|
||||
.args = try lt.type_store.appendVars(&[_]types.Var{token_contents_alias_var}),
|
||||
};
|
||||
const err_tag = types.Tag{
|
||||
.name = try lt.module_env.insertIdent(Ident.for_text("Err")),
|
||||
.args = try lt.type_store.appendVars(&[_]types.Var{str_var}),
|
||||
};
|
||||
const try_tags_range = try lt.type_store.appendTags(&[_]types.Tag{ ok_tag, err_tag });
|
||||
const try_backing_tag_union = types.TagUnion{
|
||||
.tags = try_tags_range,
|
||||
.ext = try lt.type_store.freshFromContent(.{ .structure = .empty_tag_union }),
|
||||
};
|
||||
const try_backing_var = try lt.type_store.freshFromContent(.{ .structure = .{ .tag_union = try_backing_tag_union } });
|
||||
|
||||
// Create the Try nominal type: Try(TokenContents, Str)
|
||||
const try_content = try lt.type_store.mkNominal(
|
||||
.{ .ident_idx = try_ident_idx },
|
||||
try_backing_var,
|
||||
&[_]types.Var{ token_contents_alias_var, str_var },
|
||||
builtin_module_idx,
|
||||
false,
|
||||
);
|
||||
const try_var = try lt.type_store.freshFromContent(try_content);
|
||||
|
||||
// This should succeed without TypeContainedMismatch error.
|
||||
// Before the fix, this would fail because the alias was incorrectly detected as a cycle.
|
||||
const result_idx = try lt.layout_store.addTypeVar(try_var, <.type_scope);
|
||||
const result_layout = lt.layout_store.getLayout(result_idx);
|
||||
|
||||
// Try should have a tag_union layout
|
||||
try testing.expect(result_layout.tag == .tag_union);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue