Add "$checks". Fix where $y++ could appear inside a runtime scope.

This commit is contained in:
Christoffer Lerno
2022-09-20 17:56:46 +02:00
committed by Christoffer Lerno
parent 4fa4b2a631
commit be5c82cfa6
14 changed files with 110 additions and 14 deletions

View File

@@ -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;

View File

@@ -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:

View File

@@ -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

View File

@@ -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);

View File

@@ -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 },

View File

@@ -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:

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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))

View File

@@ -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:

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.3.52"
#define COMPILER_VERSION "0.3.53"

View 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

View 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