Bug: compiler semantic analysis is checking for known nulls in a $defined context
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
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
Updated the reproduction with the context that I encountered it
This should work now, please try it.
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'
Though the above does work without the function call invocation
❯ 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'.
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.
You can try and see that there is no error now, but int x is not added.