Repository navigation
typechecker: a local bound to s[i] is a byte (#3479) - #3505
Closed
paul-hammant wants to merge 2 commits into
Closed
paul-hammant wants to merge 2 commits into
paul-hammant wants to merge 2 commits into
Conversation
When indexing a string (`s[i]`), type inference was leaving the node type
unset because it only handled `TYPE_ARRAY`. As a result, `infer_type`
returned `TYPE_UNKNOWN`, leaving any local bound to `s[i]` without a known
type until codegen defaulted it to `int` with a warning.
Infer string indexing as `TYPE_BYTE` (an unsigned 8-bit integer /
`unsigned char` in C), matching the language definition in
docs/language-reference.md ("s[i] reads one [byte]"), existing typed
contexts, and unsigned byte semantics.
Add regression test tests/regression/test_issue3479_string_index_local.ae
covering all string sources (local, parameter, struct field, function
return value, StringSeq, string[] element), high-byte indexing
("\xC3"[0] == 195), arithmetic/comparison/range match, and zero warning
verification.
Co-authored-by: paul-hammant <82182+paul-hammant@users.noreply.github.com>
`c = s[i]` left the local untyped: inference set an element type only for an array base, so a string base fell through and codegen defaulted the local to int with "unresolved type in codegen, defaulting to int", which fails `make check-examples`. The read is a byte, so the local is one, as a local bound to a byte[] element is. A high byte reads 195, not -61, and arithmetic stored back into the local wraps (88 - 100 is 244, where the int default gave -12); `s[i] as int` keeps a signed result. The test is an integration directory: a fixture binding the read from a local, a parameter, a field, a returned string and *StringSeq / string[] elements, and a script that builds it with no warning and runs it. Reworks Jules's draft (paul-hammant#49, Jules task 7456359029880539017): the fix is Jules's; the test no longer re-runs `./build/ae build --with=all` from inside itself, and the changelog and language reference say what the byte type means for arithmetic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3479.
Reworks Jules's draft (paul-hammant#49, Jules task 7456359029880539017).
The bug
c = s[i](a local, parameter, field or returned string) leftcuntyped.collect_expression_constraintsset an element type forAST_ARRAY_ACCESSonly when the base wasTYPE_ARRAY, so a string base fell through, and codegen defaulted the local tointwithwarning: unresolved type in codegen, defaulting to int. That failsmake check-examples, which blocks the Rosetta Caesar cipher in #3477.return c as inton such a local was refused too ("cannot cast unknown to int").The fix (Jules's, 4 lines)
A string base types the read as
byte, which is what the language reference sayss[i]reads.This changes behaviour, and it is consistent with
byte[]:c = s[0]; c = c - 100gives 244 for"X", where the int default gave -12. A local bound to abyte[]element (x = b[0]; x = x - 70) already wraps the same way on main;c = s[0]; c = 1000is now a compile error (byte literal out of range);s[0] + 1,s[0] == 88andmatch c { 65..=90 -> ... }are unchanged.s[i] as intkeeps a signed result.The changelog says so, and the language reference now says
s[i]reads abyte.What changed from the draft
tests/regression/test_issue3479_string_index_local.aere-ran./build/ae build --with=allfrom inside itself. That depended on the current directory, wrote intobuild/, and used--with=all, the backdoorAGENTS.mdrules out.tests/integration/string_index_local_type/has a fixture and a script. The script builds the fixture with no warning at all and runs it, and the sweep runs the fixture too.as intgives -12.Verification
return c as int("cannot cast unknown to int").unresolved type in codegen, defaulting to intwarnings.[PASS] string_index_local_type.make test: passes.make test-ae: 2071 passed, 2 failed. The two arehttp_server_h2andwindows_crt_symbols, which fail the same way on a clean main.check-examples: clean apart from an untracked local directory.ae fmt,check-docs,check-changelog,check-tests,check-warningsandcheck-raw-callsall pass.🤖 Generated with Claude Code