mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Add "$checks". Fix where $y++ could appear inside a runtime scope.
This commit is contained in:
committed by
Christoffer Lerno
parent
4fa4b2a631
commit
be5c82cfa6
@@ -290,6 +290,7 @@ bool expr_is_pure(Expr *expr)
|
||||
case EXPR_TYPEID:
|
||||
case EXPR_CT_ARG:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_CT_CHECKS:
|
||||
return true;
|
||||
case EXPR_VASPLAT:
|
||||
return true;
|
||||
|
||||
@@ -284,6 +284,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
|
||||
case EXPR_GROUP:
|
||||
case EXPR_STRINGIFY:
|
||||
case EXPR_CT_EVAL:
|
||||
case EXPR_CT_CHECKS:
|
||||
MACRO_COPY_EXPR(expr->inner_expr);
|
||||
return expr;
|
||||
case EXPR_TYPEID_INFO:
|
||||
|
||||
@@ -211,6 +211,7 @@ typedef enum
|
||||
EXPR_CATCH_UNWRAP,
|
||||
EXPR_COMPOUND_LITERAL,
|
||||
EXPR_CONST,
|
||||
EXPR_CT_CHECKS,
|
||||
EXPR_CT_CALL,
|
||||
EXPR_CT_CONV,
|
||||
EXPR_CT_IDENT,
|
||||
@@ -343,6 +344,7 @@ typedef enum
|
||||
typedef enum
|
||||
{
|
||||
SCOPE_NONE = 0,
|
||||
SCOPE_CHECKS = 1 << 0,
|
||||
SCOPE_EXPR_BLOCK = 1 << 5,
|
||||
SCOPE_MACRO = 1 << 6,
|
||||
} ScopeFlags;
|
||||
@@ -544,6 +546,7 @@ typedef enum
|
||||
TOKEN_CT_ALIGNOF, // $alignof
|
||||
TOKEN_CT_ASSERT, // $assert
|
||||
TOKEN_CT_CASE, // $case
|
||||
TOKEN_CT_CHECKS, // $checks
|
||||
TOKEN_CT_DEFAULT, // $default
|
||||
TOKEN_CT_DEFINED, // $defined
|
||||
TOKEN_CT_FOR, // $for
|
||||
|
||||
@@ -5545,6 +5545,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
case EXPR_CT_ARG:
|
||||
case EXPR_ASM:
|
||||
case EXPR_VASPLAT:
|
||||
case EXPR_CT_CHECKS:
|
||||
UNREACHABLE
|
||||
case EXPR_BUILTIN_ACCESS:
|
||||
llvm_emit_builtin_access(c, value, expr);
|
||||
|
||||
@@ -886,6 +886,18 @@ static Expr *parse_ct_sizeof(ParseContext *c, Expr *left)
|
||||
return access;
|
||||
}
|
||||
|
||||
static Expr *parse_ct_checks(ParseContext *c, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *checks = expr_new(EXPR_CT_CHECKS, c->span);
|
||||
advance_and_verify(c, TOKEN_CT_CHECKS);
|
||||
CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr);
|
||||
ASSIGN_EXPR_OR_RET(checks->inner_expr, parse_expression_list(c, true), poisoned_expr);
|
||||
CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr);
|
||||
RANGE_EXTEND_PREV(checks);
|
||||
return checks;
|
||||
}
|
||||
|
||||
static Expr *parse_ct_call(ParseContext *c, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
@@ -1762,6 +1774,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_CT_SIZEOF] = { parse_ct_sizeof, NULL, PREC_NONE },
|
||||
[TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE },
|
||||
[TOKEN_CT_DEFINED] = { parse_ct_call, NULL, PREC_NONE },
|
||||
[TOKEN_CT_CHECKS] = { parse_ct_checks, NULL, PREC_NONE },
|
||||
[TOKEN_CT_EVAL] = { parse_ct_eval, NULL, PREC_NONE },
|
||||
[TOKEN_CT_EXTNAMEOF] = { parse_ct_call, NULL, PREC_NONE },
|
||||
[TOKEN_CT_OFFSETOF] = { parse_ct_call, NULL, PREC_NONE },
|
||||
|
||||
@@ -1204,6 +1204,7 @@ Ast *parse_stmt(ParseContext *c)
|
||||
case TOKEN_CT_QNAMEOF:
|
||||
case TOKEN_CT_NAMEOF:
|
||||
case TOKEN_CT_DEFINED:
|
||||
case TOKEN_CT_CHECKS:
|
||||
case TOKEN_CT_STRINGIFY:
|
||||
case TOKEN_CT_EVAL:
|
||||
case TOKEN_TRY:
|
||||
|
||||
@@ -847,6 +847,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type)
|
||||
case EXPR_ASM:
|
||||
case EXPR_VASPLAT:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_CT_CHECKS:
|
||||
UNREACHABLE
|
||||
case EXPR_BUILTIN_ACCESS:
|
||||
|
||||
@@ -1019,6 +1020,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type)
|
||||
case EXPR_ASM:
|
||||
case EXPR_VASPLAT:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_CT_CHECKS:
|
||||
UNREACHABLE
|
||||
case EXPR_POST_UNARY:
|
||||
return recursive_may_narrow_int(expr->unary_expr.expr, type);
|
||||
|
||||
@@ -575,19 +575,14 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl)
|
||||
return false;
|
||||
}
|
||||
Decl **members = decl->bitstruct.members;
|
||||
bool success = true;
|
||||
SCOPE_START
|
||||
VECEACH(members, i)
|
||||
VECEACH(members, i)
|
||||
{
|
||||
if (!sema_analyse_bitstruct_member(context, decl, i, decl->bitstruct.overlap))
|
||||
{
|
||||
if (!sema_analyse_bitstruct_member(context, decl, i, decl->bitstruct.overlap))
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
return decl_poison(decl);
|
||||
}
|
||||
SCOPE_END;
|
||||
if (!success) return decl_poison(decl);
|
||||
return decl_ok(decl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -405,6 +405,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
|
||||
case EXPR_CONST:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_STRINGIFY:
|
||||
case EXPR_CT_CHECKS:
|
||||
return true;
|
||||
case EXPR_COND:
|
||||
return expr_list_is_constant_eval(expr->cond_expr, eval_kind);
|
||||
@@ -725,6 +726,7 @@ static bool sema_check_expr_lvalue(Expr *top_expr, Expr *expr)
|
||||
case EXPR_ASM:
|
||||
case EXPR_VASPLAT:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_CT_CHECKS:
|
||||
goto ERR;
|
||||
}
|
||||
UNREACHABLE
|
||||
@@ -827,6 +829,7 @@ bool expr_may_addr(Expr *expr)
|
||||
case EXPR_ASM:
|
||||
case EXPR_VASPLAT:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_CT_CHECKS:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
@@ -7115,6 +7118,12 @@ static inline bool sema_expr_analyse_ct_incdec(SemaContext *context, Expr *expr,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (var->var.scope_depth < context->active_scope.depth)
|
||||
{
|
||||
SEMA_ERROR(expr, "Cannot modify '%s' inside of a runtime scope.", var->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
Expr *end_value = expr_copy(start_value);
|
||||
|
||||
// Make the change.
|
||||
@@ -8022,6 +8031,33 @@ RETRY:
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static inline Expr *sema_check_exprlist(SemaContext *context, Expr *exprlist)
|
||||
{
|
||||
assert(exprlist->expr_kind == EXPR_EXPRESSION_LIST);
|
||||
Expr *failed = NULL;
|
||||
bool suppress_error = global_context.suppress_errors;
|
||||
global_context.suppress_errors = true;
|
||||
SCOPE_START_WITH_FLAGS(SCOPE_CHECKS);
|
||||
FOREACH_BEGIN(Expr *expr, exprlist->expression_list)
|
||||
if (!sema_analyse_cond_expr(context, expr))
|
||||
{
|
||||
failed = expr;
|
||||
break;
|
||||
}
|
||||
FOREACH_END();
|
||||
SCOPE_END;
|
||||
global_context.suppress_errors = suppress_error;
|
||||
return failed;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_ct_checks(SemaContext *context, Expr *expr)
|
||||
{
|
||||
Expr *err = sema_check_exprlist(context, expr->inner_expr);
|
||||
expr_rewrite_const_bool(expr, type_bool, err == NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr)
|
||||
{
|
||||
if (expr->resolve_status == RESOLVE_DONE) return expr_ok(expr);
|
||||
@@ -8426,6 +8462,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr)
|
||||
case EXPR_VASPLAT:
|
||||
SEMA_ERROR(expr, "'$vasplat' can only be used inside of macros.");
|
||||
return false;
|
||||
case EXPR_CT_CHECKS:
|
||||
return sema_expr_analyse_ct_checks(context, expr);
|
||||
case EXPR_CT_ARG:
|
||||
return sema_expr_analyse_ct_arg(context, expr);
|
||||
case EXPR_VARIANT:
|
||||
|
||||
@@ -2515,11 +2515,10 @@ static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **a
|
||||
bool success = true;
|
||||
bool suppress_error = global_context.suppress_errors;
|
||||
global_context.suppress_errors = true;
|
||||
SCOPE_START
|
||||
SCOPE_START_WITH_FLAGS(SCOPE_CHECKS)
|
||||
VECEACH(declexpr->cond_expr, j)
|
||||
{
|
||||
Expr *expr = declexpr->cond_expr[j];
|
||||
|
||||
const char *comment = directive->doc_stmt.contract.comment;
|
||||
global_context.suppress_errors = comment != NULL;
|
||||
if (!sema_analyse_cond_expr(context, expr))
|
||||
|
||||
@@ -330,6 +330,8 @@ const char *token_type_to_string(TokenType type)
|
||||
return "$assert";
|
||||
case TOKEN_CT_CASE:
|
||||
return "$case";
|
||||
case TOKEN_CT_CHECKS:
|
||||
return "$checks";
|
||||
case TOKEN_CT_DEFAULT:
|
||||
return "$default";
|
||||
case TOKEN_CT_DEFINED:
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.3.52"
|
||||
#define COMPILER_VERSION "0.3.53"
|
||||
20
test/test_suite/compile_time/ct_checks.c3t
Normal file
20
test/test_suite/compile_time/ct_checks.c3t
Normal file
@@ -0,0 +1,20 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
import std::io;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
int a;
|
||||
bool b = $checks(a = 12.0);
|
||||
var $y = 23;
|
||||
bool c = $checks(int z = 23, $y += 23, &c);
|
||||
bool d = $checks(&c, $y, int yy = 23);
|
||||
io::printfln("%s %s %s", b, $y, c);
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
store i32 0
|
||||
store i8 0
|
||||
store i8 0
|
||||
store i8 1
|
||||
20
test/test_suite2/compile_time/ct_checks.c3t
Normal file
20
test/test_suite2/compile_time/ct_checks.c3t
Normal file
@@ -0,0 +1,20 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
import std::io;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
int a;
|
||||
bool b = $checks(a = 12.0);
|
||||
var $y = 23;
|
||||
bool c = $checks(int z = 23, $y += 23, &c);
|
||||
bool d = $checks(&c, $y, int yy = 23);
|
||||
io::printfln("%s %s %s", b, $y, c);
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
store i32 0
|
||||
store i8 0
|
||||
store i8 0
|
||||
store i8 1
|
||||
Reference in New Issue
Block a user