- !!foo now works same as as ! ! foo.

- Incorrectly allowed getting pointer to a macro #2049.
This commit is contained in:
Christoffer Lerno
2025-03-16 23:57:30 +01:00
parent 425676a98d
commit 82cc49b388
6 changed files with 85 additions and 7 deletions

View File

@@ -36,6 +36,7 @@
- Order of attribute declaration is changed for `alias`.
- Added `LANGUAGE_DEV_VERSION` env constant.
- Rename `anyfault` -> `fault`.
- `!!foo` now works same as as `! ! foo`.
### Fixes
- Fix address sanitizer to work on MachO targets (e.g. MacOS).
@@ -48,6 +49,7 @@
- Crash when trying to convert a struct slice to a vector #2039.
- Crash resolving a method on `Foo[2]` when `Foo` is distinct #2042.
- Bug due to missing cast when doing `$i[$x] = $z`.
- Incorrectly allowed getting pointer to a macro #2049.
### Stdlib changes
- `new_*` functions in general moved to version without `new_` prefix.

View File

@@ -239,6 +239,7 @@ UnaryOp unary_op[TOKEN_LAST + 1] = {
[TOKEN_AND] = UNARYOP_TADDR,
[TOKEN_BIT_NOT] = UNARYOP_BITNEG,
[TOKEN_BANG] = UNARYOP_NOT,
[TOKEN_BANGBANG] = UNARYOP_NOT,
[TOKEN_MINUS] = UNARYOP_NEG,
[TOKEN_PLUS] = UNARYOP_PLUS,
[TOKEN_PLUSPLUS] = UNARYOP_INC,

View File

@@ -701,15 +701,21 @@ static Expr *parse_unary_expr(ParseContext *c, Expr *left)
{
ASSERT(!left && "Did not expect a left hand side!");
bool is_bangbang = tok_is(c, TOKEN_BANGBANG);
Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY);
unary->unary_expr.operator = unaryop_from_token(c->tok);
advance(c);
Expr *right_side = parse_precedence(c, PREC_UNARY);
CHECK_EXPR_OR_RET(right_side);
unary->unary_expr.expr = right_side;
RANGE_EXTEND_PREV(unary);
if (is_bangbang)
{
Expr *outer = expr_new_expr(EXPR_UNARY, unary);
outer->unary_expr = (ExprUnary) { .expr = unary, .operator = UNARYOP_NOT };
return outer;
}
return unary;
}
@@ -757,7 +763,7 @@ static Expr *parse_ternary_expr(ParseContext *c, Expr *left_side)
// If we have no expression following *or* it is a '!' followed by no expression
// in this case it's an optional expression.
if (!rules[c->tok].prefix || (c->tok == TOKEN_BANG && !rules[peek(c)].prefix))
if (!rules[c->tok].prefix || ((c->tok == TOKEN_BANG || c->tok == TOKEN_BANGBANG) && !rules[peek(c)].prefix))
{
expr->expr_kind = EXPR_OPTIONAL;
expr->inner_expr = left_side;
@@ -1958,7 +1964,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
[TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL },
[TOKEN_MINUSMINUS] = { parse_unary_expr, parse_post_unary, PREC_CALL },
[TOKEN_LPAREN] = { parse_grouping_expr, parse_call_expr, PREC_CALL },
[TOKEN_BANGBANG] = { NULL, parse_force_unwrap_expr, PREC_CALL },
[TOKEN_BANGBANG] = { parse_unary_expr, parse_force_unwrap_expr, PREC_CALL },
[TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL },
[TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE },
[TOKEN_PLUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE },

View File

@@ -7193,12 +7193,16 @@ static const char *sema_addr_check_may_take(Expr *inner)
case EXPR_ACCESS_RESOLVED:
{
Decl *decl = inner->access_resolved_expr.ref;
if (decl->decl_kind == DECL_FUNC)
switch (decl->decl_kind)
{
if (decl->func_decl.attr_interface_method) return NULL;
return "Taking the address of a method should be done through the type e.g. '&Foo.method' not through the value.";
case DECL_FUNC:
if (decl->func_decl.attr_interface_method) return NULL;
return "Taking the address of a method should be done through the type e.g. '&Foo.method' not through the value.";
case DECL_MACRO:
return "It's not possible to take the address of a macro.";
default:
return sema_addr_check_may_take(inner->access_resolved_expr.parent);
}
return sema_addr_check_may_take(inner->access_resolved_expr.parent);
}
case EXPR_SUBSCRIPT_ADDR:
return NULL;

View File

@@ -0,0 +1,37 @@
// #target: macos-x64
module test;
import std;
fn void test(bool b) {}
fn void main()
{
int a;
test(!a);
test(!!a);
test(!!!a);
test(!!!!a);
}
/* #expect: test.ll
define void @test.main() #0 {
entry:
%a = alloca i32, align 4
store i32 0, ptr %a, align 4
%0 = load i32, ptr %a, align 4
%i2nb = icmp eq i32 %0, 0
%1 = zext i1 %i2nb to i8
call void @test.test(i8 zeroext %1)
%2 = load i32, ptr %a, align 4
%i2b = icmp ne i32 %2, 0
%3 = zext i1 %i2b to i8
call void @test.test(i8 zeroext %3)
%4 = load i32, ptr %a, align 4
%i2nb1 = icmp eq i32 %4, 0
%5 = zext i1 %i2nb1 to i8
call void @test.test(i8 zeroext %5)
%6 = load i32, ptr %a, align 4
%i2b2 = icmp ne i32 %6, 0
%7 = zext i1 %i2b2 to i8
call void @test.test(i8 zeroext %7)
ret void
}

View File

@@ -0,0 +1,28 @@
import std::io;
// Issue #2049
struct Container
{
int[] items;
}
macro int Container.get(&this, usz i) @operator([])
{
return this.items[i];
}
fn int Container.get2(&this, usz i)
{
return this.items[i];
}
alias ProcGetItem = fn int(usz);
fn void process(int input, ProcGetItem getter)
{
}
fn void main()
{
Container c = { .items = { 2, 3, 4, 5 } };
process(10, &c.get); // #error: It's not possible to take the address
}