mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Convert issue8656 to type-checker test
Move the regression test from a CLI test to a type-checker unit test since it only tests type checking behavior. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
930fd0865e
commit
d0d53b20ef
4 changed files with 92 additions and 85 deletions
|
|
@ -43,4 +43,5 @@ test "check tests" {
|
|||
std.testing.refAllDecls(@import("test/instantiate_tag_union_test.zig"));
|
||||
std.testing.refAllDecls(@import("test/where_clause_test.zig"));
|
||||
std.testing.refAllDecls(@import("test/recursive_alias_test.zig"));
|
||||
std.testing.refAllDecls(@import("test/generalize_redirect_test.zig"));
|
||||
}
|
||||
|
|
|
|||
91
src/check/test/generalize_redirect_test.zig
Normal file
91
src/check/test/generalize_redirect_test.zig
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//! Regression test for issue #8656: rank panic when variable redirects to higher-rank variable.
|
||||
//!
|
||||
//! The original bug was: when a variable added to the var_pool at rank 1
|
||||
//! was later redirected (via setVarRedirect) to a variable at rank 2,
|
||||
//! generalization would try to add the resolved variable at rank 2 to the
|
||||
//! tmp_var_pool which only goes up to rank 1, causing a panic.
|
||||
|
||||
const std = @import("std");
|
||||
const TestEnv = @import("./TestEnv.zig");
|
||||
|
||||
test "nested lambda with higher-rank variables does not panic during generalization" {
|
||||
// This code structure triggered the bug in the original report.
|
||||
// It involves nested lambdas that create rank-2 variables, pattern matching
|
||||
// on tuples, and recursive functions.
|
||||
//
|
||||
// The key pattern is:
|
||||
// 1. A function with a nested lambda (ret) that creates rank-2 type variables
|
||||
// 2. Pattern matching on tuples that exercises the type checker
|
||||
// 3. Recursive calls that can cause variable redirects across ranks
|
||||
const source =
|
||||
\\{
|
||||
\\ Maybe(t) : [
|
||||
\\ Some(t),
|
||||
\\ None,
|
||||
\\ ]
|
||||
\\
|
||||
\\ TokenContents : [
|
||||
\\ NewlineToken,
|
||||
\\ SymbolsToken(Str),
|
||||
\\ SnakeCaseIdentToken(Str),
|
||||
\\ EndOfFileToken,
|
||||
\\ ]
|
||||
\\
|
||||
\\ TokenizerResult : (
|
||||
\\ Try(TokenContents, Str),
|
||||
\\ U64,
|
||||
\\ U64,
|
||||
\\ )
|
||||
\\
|
||||
\\ get_next_token : List(U8), U64 -> TokenizerResult
|
||||
\\ get_next_token = |file, index| {
|
||||
\\ match List.get(file, index) {
|
||||
\\ Ok('\n') => (Ok(NewlineToken), index, index + 1)
|
||||
\\ Err(_) => (Ok(EndOfFileToken), index, index)
|
||||
\\ }
|
||||
\\ }
|
||||
\\
|
||||
\\ tokenize_identifier = |file, index, acc, start_index| {
|
||||
\\ char = List.get(file, index)
|
||||
\\ ret = || {
|
||||
\\ match Str.from_utf8(acc) {
|
||||
\\ Ok(str) => (Ok(SnakeCaseIdentToken(str)), start_index, index)
|
||||
\\ Err(_) => (Err("Invalid UTF8"), start_index, index)
|
||||
\\ }
|
||||
\\ }
|
||||
\\ match char {
|
||||
\\ Ok(c) => {
|
||||
\\ if ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') or (c == '_') {
|
||||
\\ tokenize_identifier(file, index + 1, List.append(acc, c), start_index)
|
||||
\\ } else {
|
||||
\\ ret()
|
||||
\\ }
|
||||
\\ }
|
||||
\\ _ => ret()
|
||||
\\ }
|
||||
\\ }
|
||||
\\
|
||||
\\ parse_pattern = |file, tokenizer_result| {
|
||||
\\ (token, _, index) = tokenizer_result
|
||||
\\ match token {
|
||||
\\ Ok(SnakeCaseIdentToken(ident)) => {
|
||||
\\ match get_next_token(file, index) {
|
||||
\\ (Ok(SymbolsToken(":")), _, index2) => Ok((ident, Some("type"), index2))
|
||||
\\ _ => Ok((ident, None, index))
|
||||
\\ }
|
||||
\\ }
|
||||
\\ _ => Err("expected pattern")
|
||||
\\ }
|
||||
\\ }
|
||||
\\
|
||||
\\ parse_pattern
|
||||
\\}
|
||||
;
|
||||
var test_env = try TestEnv.initExpr("Test", source);
|
||||
defer test_env.deinit();
|
||||
|
||||
// If we get here without panicking, the test passes.
|
||||
// The bug would cause a panic during type checking with:
|
||||
// "trying to add var at rank 2, but current rank is 1"
|
||||
try test_env.assertNoErrors();
|
||||
}
|
||||
|
|
@ -238,11 +238,6 @@ pub const io_spec_tests = [_]TestSpec{
|
|||
.io_spec = "0<short|1>short|0<|1>",
|
||||
.description = "Regression test: Stdin.line! in while loop with short input (small string optimization)",
|
||||
},
|
||||
.{
|
||||
.roc_file = "test/fx/issue8656.roc",
|
||||
.io_spec = "1>ok",
|
||||
.description = "Regression test: rank panic when variable redirects to higher-rank variable during generalization",
|
||||
},
|
||||
};
|
||||
|
||||
/// Get the total number of IO spec tests
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
app [main!] { pf: platform "./platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
|
||||
# Regression test for issue #8656
|
||||
# Tests that generalization handles variables that redirect to higher-rank
|
||||
# variables without panicking.
|
||||
#
|
||||
# The original bug was: when a variable added to the var_pool at rank 1
|
||||
# was later redirected (via setVarRedirect) to a variable at rank 2,
|
||||
# generalization would try to add the resolved variable at rank 2 to the
|
||||
# tmp_var_pool which only goes up to rank 1, causing a panic.
|
||||
|
||||
main! = || {
|
||||
Stdout.line!("ok")
|
||||
}
|
||||
|
||||
# The following code structure triggered the bug in the original report.
|
||||
# It involves complex type definitions with nested tuples and recursive functions.
|
||||
|
||||
Maybe(t) : [
|
||||
Some(t),
|
||||
None,
|
||||
]
|
||||
|
||||
TokenContents : [
|
||||
NewlineToken,
|
||||
SymbolsToken(Str),
|
||||
SnakeCaseIdentToken(Str),
|
||||
EndOfFileToken,
|
||||
]
|
||||
|
||||
TokenizerResult : (
|
||||
Try(TokenContents, Str),
|
||||
U64,
|
||||
U64,
|
||||
)
|
||||
|
||||
get_next_token : List(U8), U64 -> TokenizerResult
|
||||
get_next_token = |file, index| {
|
||||
match List.get(file, index) {
|
||||
Ok('\n') => (Ok(NewlineToken), index, index + 1)
|
||||
Err(_) => (Ok(EndOfFileToken), index, index)
|
||||
}
|
||||
}
|
||||
|
||||
# This function has a nested lambda (ret) that creates rank-2 variables
|
||||
tokenize_identifier = |file, index, acc, start_index| {
|
||||
char = List.get(file, index)
|
||||
ret = || {
|
||||
match Str.from_utf8(acc) {
|
||||
Ok(str) => (Ok(SnakeCaseIdentToken(str)), start_index, index)
|
||||
Err(_) => (Err("Invalid UTF8"), start_index, index)
|
||||
}
|
||||
}
|
||||
match char {
|
||||
Ok(c) => {
|
||||
if ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') or (c == '_') {
|
||||
tokenize_identifier(file, index + 1, List.append(acc, c), start_index)
|
||||
} else {
|
||||
ret()
|
||||
}
|
||||
}
|
||||
_ => ret()
|
||||
}
|
||||
}
|
||||
|
||||
# This function with pattern matching on tuples exercises the type checker
|
||||
parse_pattern = |file, tokenizer_result| {
|
||||
(token, _, index) = tokenizer_result
|
||||
match token {
|
||||
Ok(SnakeCaseIdentToken(ident)) => {
|
||||
match get_next_token(file, index) {
|
||||
(Ok(SymbolsToken(":")), _, index2) => Ok((ident, Some("type"), index2))
|
||||
_ => Ok((ident, None, index))
|
||||
}
|
||||
}
|
||||
_ => Err("expected pattern")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue