$is_const is deprecated in favour of @is_const based on $defined.

`$foo` variables could be assigned non-compile time values.
`$foo[0] = ...` was incorrectly requiring that the assigned values were compile time constants.
This commit is contained in:
Christoffer Lerno
2025-07-10 18:31:38 +02:00
parent 70159c00cc
commit 988549599d
5 changed files with 50 additions and 16 deletions

View File

@@ -20,6 +20,12 @@ macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
macro bool @is_lvalue(#value) => $defined(#value = #value);
macro bool @assignable_to(#foo, $Type) @const @builtin => $defined(*&&($Type){} = #foo);
macro bool @is_const(#foo) @const @builtin
{
var $v;
return $defined($v = #foo);
}
macro promote_int(x)
{

View File

@@ -15,6 +15,8 @@
- Formatting option "%h" now supports pointers.
- Improve error on unsigned implicit conversion to signed.
- Update error message for struct initialization #2286
- `$is_const` is deprecated in favour of `@is_const` based on `$defined`.
### Fixes
- mkdir/rmdir would not work properly with substring paths on non-windows platforms.
- Hex string formatter check incorrectly rejected slices.
@@ -41,6 +43,8 @@
- Function pointers are now compile time constants.
- Splat 8 arguments can sometimes cause incorrect behaviour in the compiler. #2283
- Correctly poison the analysis after a failed $assert or $error. #2284
- `$foo` variables could be assigned non-compile time values.
- `$foo[0] = ...` was incorrectly requiring that the assigned values were compile time constants.
### Stdlib changes
- Improve contract for readline. #2280

View File

@@ -1097,7 +1097,7 @@ static Expr *parse_access_expr(ParseContext *c, Expr *left, SourceSpan lhs_start
return access_expr;
}
static Expr *parse_ct_ident(ParseContext *c, Expr *left, SourceSpan lhs_span)
static Expr *parse_ct_ident(ParseContext *c, Expr *left, SourceSpan lhs_span UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
if (try_consume(c, TOKEN_CT_CONST_IDENT))
@@ -1112,7 +1112,7 @@ static Expr *parse_ct_ident(ParseContext *c, Expr *left, SourceSpan lhs_span)
}
static Expr *parse_hash_ident(ParseContext *c, Expr *left, SourceSpan lhs_span)
static Expr *parse_hash_ident(ParseContext *c, Expr *left, SourceSpan lhs_span UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
Expr *expr = EXPR_NEW_TOKEN(EXPR_HASH_IDENT);
@@ -1125,7 +1125,7 @@ static Expr *parse_hash_ident(ParseContext *c, Expr *left, SourceSpan lhs_span)
/**
* ct_eval ::= CT_EVAL '(' expr ')'
*/
static Expr *parse_ct_eval(ParseContext *c, Expr *left, SourceSpan lhs_start)
static Expr *parse_ct_eval(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_EVAL);
@@ -1138,7 +1138,7 @@ static Expr *parse_ct_eval(ParseContext *c, Expr *left, SourceSpan lhs_start)
}
static Expr *parse_ct_defined(ParseContext *c, Expr *left, SourceSpan lhs_start)
static Expr *parse_ct_defined(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
Expr *defined = expr_new(EXPR_CT_DEFINED, c->span);
@@ -1153,7 +1153,7 @@ static Expr *parse_ct_defined(ParseContext *c, Expr *left, SourceSpan lhs_start)
*
* Note that this is tranformed to $typeof(expr).sizeof.
*/
static Expr *parse_ct_sizeof(ParseContext *c, Expr *left, SourceSpan lhs_start)
static Expr *parse_ct_sizeof(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
Expr *access = expr_new(EXPR_ACCESS_UNRESOLVED, c->span);
@@ -1178,7 +1178,7 @@ static Expr *parse_ct_sizeof(ParseContext *c, Expr *left, SourceSpan lhs_start)
/**
* ct_is_const ::= CT_IS_CONST '(' expr ')'
*/
static Expr *parse_ct_is_const(ParseContext *c, Expr *left, SourceSpan lhs_start)
static Expr *parse_ct_is_const(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
Expr *checks = expr_new(EXPR_CT_IS_CONST, c->span);
@@ -1187,13 +1187,14 @@ static Expr *parse_ct_is_const(ParseContext *c, Expr *left, SourceSpan lhs_start
ASSIGN_EXPR_OR_RET(checks->inner_expr, parse_expr(c), poisoned_expr);
CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr);
RANGE_EXTEND_PREV(checks);
SEMA_DEPRECATED(checks, "The $is_const macro is deprecated. Use @is_const(...) instead.");
return checks;
}
/**
* ct_checks ::= CT_EMBED '(' constant_expr (',' constant_expr)? ')'
*/
static Expr *parse_ct_embed(ParseContext *c, Expr *left, SourceSpan lhs_start)
static Expr *parse_ct_embed(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
Expr *embed = expr_new(EXPR_EMBED, c->span);
@@ -1213,7 +1214,7 @@ static Expr *parse_ct_embed(ParseContext *c, Expr *left, SourceSpan lhs_start)
* ct_call ::= (CT_ALIGNOF | CT_FEATURE | CT_EXTNAMEOF | CT_OFFSETOF | CT_NAMEOF | CT_QNAMEOF) '(' flat_path ')'
* flat_path ::= expr ('.' primary) | '[' expr ']')*
*/
static Expr *parse_ct_call(ParseContext *c, Expr *left, SourceSpan lhs_start)
static Expr *parse_ct_call(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_CALL);
@@ -1230,7 +1231,7 @@ static Expr *parse_ct_call(ParseContext *c, Expr *left, SourceSpan lhs_start)
return expr;
}
static Expr *parse_ct_assignable(ParseContext *c, Expr *left, SourceSpan lhs_start)
static Expr *parse_ct_assignable(ParseContext *c, Expr *left, SourceSpan lhs_start UNUSED)
{
ASSERT(!left && "Unexpected left hand side");
Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_ASSIGNABLE);

View File

@@ -92,7 +92,7 @@ static bool sema_expr_check_shift_rhs(SemaContext *context, Expr *expr, Expr *le
is_assign);
static bool sema_expr_analyse_and_or(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref);
static bool sema_expr_analyse_slice_assign(SemaContext *context, Expr *expr, Type *left_type, Expr *right, bool *failed_ref);
static bool sema_expr_analyse_ct_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right);
static bool sema_expr_analyse_ct_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref);
static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref);
static bool sema_expr_analyse_comp(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref);
static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, BinaryOp operator);
@@ -6538,17 +6538,20 @@ bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type
return true;
}
static bool sema_expr_analyse_ct_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right)
static bool sema_expr_analyse_ct_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref)
{
// Do regular lvalue evaluation of the identifier
if (!sema_analyse_expr_lvalue(context, left, NULL)) return false;
ASSERT_SPAN(left, left->resolve_status == RESOLVE_DONE);
// Evaluate right side to using inference from last type.
if (!sema_analyse_inferred_expr(context, left->type, right)) return false;
if (!expr_is_runtime_const(right))
{
if (failed_ref) return *failed_ref = true, false;
RETURN_SEMA_ERROR(right, "You can only assign constants to a compile time variable.");
}
Decl *ident = left->ct_ident_expr.decl;
ident->var.init_expr = right;
expr_replace(expr, right);
ident->type = right->type;
@@ -6565,7 +6568,7 @@ static bool sema_expr_analyse_ct_subscript_rhs(SemaContext *context, Decl *ct_va
{
if (!sema_analyse_expr_rhs(context, type_get_indexed_type(ct_var->type), right, false, NULL, false)) return false;
}
if (!sema_cast_const(right))
if (!expr_is_runtime_const(right))
{
RETURN_SEMA_ERROR(right, "The argument must be a constant value.");
}
@@ -6654,7 +6657,7 @@ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *lef
{
case EXPR_CT_IDENT:
// $foo = ...
return sema_expr_analyse_ct_identifier_assign(context, expr, left, right);
return sema_expr_analyse_ct_identifier_assign(context, expr, left, right, failed_ref);
case EXPR_CT_SUBSCRIPT:
return sema_expr_analyse_ct_subscript_assign(context, expr, left, right);
default:

View File

@@ -0,0 +1,20 @@
fn void test1()
{
int a;
var $x;
$x = a; // #error: only assign constants to a compile time
}
fn void test2()
{
int a;
int[2] $x = { 1, 2 };
$x[0] = a; // #error: argument must be a constant value
}
int g;
fn void test3()
{
int*[2] $x = { &g + 1, null };
int* $y = &g + 1;
$x[0] = &g + 1;
}