Skip to content

make unary prefix operators bind tighter than the as/is cast#74

Open
Cintu07 wants to merge 2 commits into
rux-lang:devfrom
Cintu07:fix-cast-precedence
Open

make unary prefix operators bind tighter than the as/is cast#74
Cintu07 wants to merge 2 commits into
rux-lang:devfrom
Cintu07:fix-cast-precedence

Conversation

@Cintu07

@Cintu07 Cintu07 commented May 31, 2026

Copy link
Copy Markdown
Contributor

found a precedence bug: *p as int parses as *(p as int) instead of
(*p) as int, so dereferencing through a cast doesnt compile:

var x: int32 = 42;
let p = &x;
return *p as int;   // error: '*' (dereference) applied to non-pointer type 'int'

(*p) as int works, and *p on its own works, only the combo breaks. same thing
hits -, ~, !, & in front of a cast. the parser ladder had ParseCast below
ParseUnary so the cast bound tighter than the prefix ops.

reordered it to ParseExp -> ParseCast -> ParseUnary -> ParsePostfix so the prefix
applies first, matching the usual precedence and the pointer examples in the
docs. chained casts, casts as binary operands, and ** are unchanged, and the
existing io/pow/unicode suites still pass.

added Tests/CastPrecedence + run_cast_precedence_test.sh, wired into all 3 CI
workflows.

@musicvano

musicvano commented May 31, 2026

Copy link
Copy Markdown
Member

I encountered this problem. Let's create a poll on the Discord server. We should define the priority of the as keyword. Because it impacts on the source code.
a + b as int8 is it (a + b) as int8 or a + (b as int8)?

@Cintu07

Cintu07 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

yeah worth pinning down. one scope note first: this PR only changes the
unary-vs-as level (*p as int -> (*p) as int). the a + b as int8 case is a
different level (as vs +), and it already parses as a + (b as int8) today,
before and after this PR. confirmed with --dump-ast:

a + b as int8
BinaryExpr +
IdentExpr a
CastExpr as int8
IdentExpr b

so rux already matches rust here: unary binds tighter than as, and as binds
tighter than the arithmetic ops:
*p as int -> (*p) as int
a + b as int8 -> a + (b as int8)

my vote is keep it as-is, matches rust/c and is the least surprising. are you
putting the poll up, or do you want me to run it on discord? happy either way.

@pascalecu

Copy link
Copy Markdown
Member

The poll concluded just now, and the final decision is to parse a + b as uint8 as (a + b) as uint8.

@spatulari

Copy link
Copy Markdown
Member

please resolve the conflicts.

@Cintu07

Cintu07 commented Jun 7, 2026

Copy link
Copy Markdown
Contributor Author

okok i will, sorry for late respomse

@yydev-official

Copy link
Copy Markdown
Contributor

I this more of a quality of life merge?

@Cintu07 Cintu07 force-pushed the fix-cast-precedence branch from 08ec898 to 35b2815 Compare June 8, 2026 10:45
@Cintu07

Cintu07 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

The poll concluded just now, and the final decision is to parse a + b as uint8 as (a + b) as uint8.

this PR only changes the unary-vs-as level
(*p as int -> (*p) as int), it doesnt touch where as sits relative to + and *,
so the poll result (a + b as int8 -> (a + b) as int8) is a separate change.

happy to do that one too. one thing to settle first, if as just becomes looser
than +, then a as int8 + b stops parsing (the + b gets orphaned, youd need
parens). cleanest fix that gives the poll result without breaking that is to put
as at the same precedence as the additive ops, left associative:
a + b as int8 -> (a + b) as int8 (poll)
a as int8 + b -> (a as int8) + b (still works)
want me to fold that into this PR or open a separate one so this stays scoped to
the unary fix? also rebased, conflicts are resolved

@Cintu07

Cintu07 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

I this more of a quality of life merge?

its a bug fix, not just QoL. right now p as int doesnt compile at all, it errors
"'
' (dereference) applied to non-pointer type" because it parses as *(p as int).
same for -x as T, ~x as T and so it unblocks real code

`*p as int` parsed as `*(p as int)` instead of `(*p) as int`, so dereferencing
through a cast failed to compile:

    var x: int32 = 42;
    let p = &x;
    return *p as int;   // error: '*' (dereference) applied to non-pointer type 'int'

`(*p) as int` and `*p` on its own both worked; only the combination broke. The
same affected -, ~, !, and & in front of a cast. The cause was the precedence
ladder placing ParseCast below ParseUnary, so the cast bound tighter than the
prefix operators.

Reorder the ladder to ParseExp -> ParseCast -> ParseUnary -> ParsePostfix so a
prefix operator applies before the cast, matching the usual precedence and the
language's own pointer examples. Chained casts, casts as binary operands, and
** are unchanged; the existing I/O, pow and unicode suites still pass.

Adds Tests/CastPrecedence and Tests/run_cast_precedence_test.sh, wired into CI
on all three platforms.
@Cintu07 Cintu07 force-pushed the fix-cast-precedence branch from 35b2815 to 5a5bb01 Compare June 8, 2026 13:31
@Natuworkguy Natuworkguy requested a review from pascalecu June 10, 2026 00:55
@pascalecu

Copy link
Copy Markdown
Member

Right now this has merge conflicts. Could you rebase on dev, convert your test to the new format (i.e. Test.cmake instead of *.sh) and undo your changes in the workflows (as now it all goes through ctest)? Otherwise, the code looks good, I'll merge it if you apply the aforementioned suggestions. @Cintu07

@pascalecu pascalecu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above.

@Natuworkguy

Copy link
Copy Markdown
Member

I resolved the conflicts

@spatulari

Copy link
Copy Markdown
Member

building doesn't work on linux, macOs and Windows, please fix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants