c3c icon indicating copy to clipboard operation
c3c copied to clipboard

Bug: compiler semantic analysis is checking for known nulls in a $defined context

Open cheese3660 opened this issue 2 weeks ago • 8 comments

I think this is obviously a bug
```c
1: <*
2:  @require $defined((Value){}.hash()) : `No .hash function found on the value`
                      ^^^^^^^^^
(/usr/lib/c3c/lib/std/collections/hashset.c3:2:20) Error: This value is known to be null so you cannot dereference it.

246:     List { LoweredSubAutomaton }* sub_automata;
247:     List { CharacterClass }* character_internment_cache;
248:     DString* string_interment_cache;
249:     HashSet { RegexNode* }* failed_string_conversions;
         ^^^^^^^^^^^^^^^^^^^^^^^
(/media/windata/Projects/c3/regex/regex.c3l/src/runtime/lowering.c3:249:5) Note: Inlined from here.

In a defined context like the above, it shouldn't matter if the value is null

cheese3660 avatar Dec 09 '25 01:12 cheese3660

Wait my reproduction there might be incorrect, because I was doing it in the shell, thats why it was ellided as the shell substituted nothing for $defined

I'll write a repro that doesnt have that issue in a bit, because I did encounter this outside the given repro

cheese3660 avatar Dec 09 '25 22:12 cheese3660

Updated the reproduction with the context that I encountered it

cheese3660 avatar Dec 09 '25 22:12 cheese3660

This should work now, please try it.

lerno avatar Dec 09 '25 23:12 lerno

The null deref error is now gone, but another one has taken its place (now using proper escaping of the $ so the $defined actually goes in

❯ echo 'int x @if($defined((char*){}.is_digit()));' | ./c3c/build/c3c compile -
1: int x @if($defined((char*){}.is_digit()));
                      ^^^^^^^^^^^^^^^^^^
(<stdin>:1:20) Error: There is no member or method 'is_digit' on 'char'

cheese3660 avatar Dec 10 '25 00:12 cheese3660

Though the above does work without the function call invocation

cheese3660 avatar Dec 10 '25 00:12 cheese3660

❯ echo 'int x @if($defined((char*){}.is_digit()));' | ./c3c/build/c3c compile - 1: int x @if($defined((char*){}.is_digit())); ^^^^^^^^^^^^^^^^^^ (:1:20) Error: There is no member or method 'is_digit' on 'char'

Although it does work if wrapped in main

echo 'fn void main() { int x @if($defined((char*){}.is_digit())); }' | c3c compile -
Program linked to executable './stdin_file'.

ManuLinares avatar Dec 10 '25 03:12 ManuLinares

Well, yes and no. This could be improved, so I fixed it. On the other hand, it will return false when called on the top level and true if it is called in a function. This is because the globals are all registered before the methods are resolved. We get one or the other, consider this:

int x @if($defined(int.foo));
fn int int.foo(i) @if($defined(x)) => i * i;

With the current ordering, neither will be added, but this works

int x @if($defined(true));
fn int int.foo(i) @if($defined(x)) => i * i;

If we reverse the ordering, so that methods are registered first, then again neither will be added. However, if methods are ordered first, then this would only yield x and not the foo method.

int x @if($defined(true));
fn int int.foo(i) @if($defined(x)) => i * i;

Each ordering is a trade-off. The nature of $defined is that it can only say what's already there, not what might be there, and there is no real way around it.

lerno avatar Dec 12 '25 23:12 lerno

You can try and see that there is no error now, but int x is not added.

lerno avatar Dec 12 '25 23:12 lerno