mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
1134 lines
31 KiB
C
1134 lines
31 KiB
C
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
|
// Use of this source code is governed by a LGPLv3.0
|
|
// a copy of which can be found in the LICENSE file.
|
|
|
|
#include "compiler_internal.h"
|
|
|
|
static inline bool expr_list_is_constant_eval(Expr **exprs);
|
|
static inline bool expr_unary_addr_is_constant_eval(Expr *expr);
|
|
static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, ArraySize index, bool from_back);
|
|
|
|
const char *expr_kind_to_string(ExprKind kind)
|
|
{
|
|
switch (kind)
|
|
{
|
|
case EXPR_ACCESS_UNRESOLVED: return "access_unresolved";
|
|
case EXPR_ACCESS_RESOLVED: return "access_resolved";
|
|
case EXPR_ASM: return "asm";
|
|
case EXPR_BENCHMARK_HOOK: return "benchmark_hook";
|
|
case EXPR_BINARY: return "binary";
|
|
case EXPR_BITACCESS: return "bitaccess";
|
|
case EXPR_BITASSIGN: return "bitassign";
|
|
case EXPR_BUILTIN: return "builtin";
|
|
case EXPR_BUILTIN_ACCESS: return "builtin_access";
|
|
case EXPR_CALL: return "call";
|
|
case EXPR_CAST: return "cast";
|
|
case EXPR_CATCH: return "catch_unwrap";
|
|
case EXPR_CATCH_UNRESOLVED: return "catch_unresolved";
|
|
case EXPR_COMPILER_CONST: return "compiler_const";
|
|
case EXPR_COMPOUND_LITERAL: return "compound_litera";
|
|
case EXPR_COND: return "cond";
|
|
case EXPR_CONST: return "const";
|
|
case EXPR_TYPECALL: return "typecall";
|
|
case EXPR_CT_ARG: return "ct_arg";
|
|
case EXPR_CT_CALL: return "ct_call";
|
|
case EXPR_CT_CASTABLE: return "ct_castable";
|
|
case EXPR_CT_DEFINED: return "ct_defined";
|
|
case EXPR_CT_EVAL: return "ct_eval";
|
|
case EXPR_CT_IDENT: return "ct_ident";
|
|
case EXPR_CT_IS_CONST: return "ct_is_const";
|
|
case EXPR_DECL: return "decl";
|
|
case EXPR_DEFAULT_ARG: return "default_arg";
|
|
case EXPR_DESIGNATED_INITIALIZER_LIST: return "designated_initializer_list";
|
|
case EXPR_DESIGNATOR: return "designator";
|
|
case EXPR_DISCARD: return "discard";
|
|
case EXPR_EMBED: return "embed";
|
|
case EXPR_VECTOR_TO_ARRAY: return "vector_to_array";
|
|
case EXPR_SLICE_TO_VEC_ARRAY: return "slice_to_vec_array";
|
|
case EXPR_SCALAR_TO_VECTOR: return "scalar_to_vector";
|
|
case EXPR_EXPRESSION_LIST: return "expression_list";
|
|
case EXPR_FORCE_UNWRAP: return "force_unwrap";
|
|
case EXPR_FLOAT_TO_INT: return "float_to_int";
|
|
case EXPR_GENERIC_IDENT: return "generic_ident";
|
|
case EXPR_HASH_IDENT: return "hash_ident";
|
|
case EXPR_IDENTIFIER: return "identifier";
|
|
case EXPR_UNRESOLVED_IDENTIFIER: return "unresolved_identifier";
|
|
case EXPR_INITIALIZER_LIST: return "initializer_list";
|
|
case EXPR_INT_TO_FLOAT: return "int_to_float";
|
|
case EXPR_INT_TO_PTR: return "int_to_ptr";
|
|
case EXPR_PTR_TO_INT: return "ptr_to_int";
|
|
case EXPR_ANYFAULT_TO_FAULT: return "anyfault_to_fault";
|
|
case EXPR_LAMBDA: return "lambda";
|
|
case EXPR_LAST_FAULT: return "last_fault";
|
|
case EXPR_MACRO_BLOCK: return "macro_block";
|
|
case EXPR_MACRO_BODY: return "macro_body";
|
|
case EXPR_MACRO_BODY_EXPANSION: return "macro_body_expansion";
|
|
case EXPR_MAKE_ANY: return "make_any";
|
|
case EXPR_MAKE_SLICE: return "make_slice";
|
|
case EXPR_MEMBER_GET: return "member_get";
|
|
case EXPR_NAMED_ARGUMENT: return "named_argument";
|
|
case EXPR_NOP: return "nop";
|
|
case EXPR_OPERATOR_CHARS: return "operator_chars";
|
|
case EXPR_OPTIONAL: return "optional";
|
|
case EXPR_ENUM_FROM_ORD: return "enum_from_ord";
|
|
case EXPR_OTHER_CONTEXT: return "other_context";
|
|
case EXPR_POINTER_OFFSET: return "pointer_offset";
|
|
case EXPR_ADDR_CONVERSION: return "addr_conversion";
|
|
case EXPR_POISONED: return "poisoned";
|
|
case EXPR_PTR_ACCESS: return "ptr_access";
|
|
case EXPR_POST_UNARY: return "post_unary";
|
|
case EXPR_RETHROW: return "rethrow";
|
|
case EXPR_RETVAL: return "retval";
|
|
case EXPR_RVALUE: return "rvalue";
|
|
case EXPR_RECAST: return "recast";
|
|
case EXPR_SLICE: return "slice";
|
|
case EXPR_SLICE_LEN: return "slice_len";
|
|
case EXPR_SLICE_ASSIGN: return "slice_assign";
|
|
case EXPR_SLICE_COPY: return "slice_copy";
|
|
case EXPR_SPLAT: return "splat";
|
|
case EXPR_STRINGIFY: return "stringify";
|
|
case EXPR_SUBSCRIPT: return "subscript";
|
|
case EXPR_SUBSCRIPT_ADDR: return "subscript_addr";
|
|
case EXPR_SUBSCRIPT_ASSIGN: return "subscript_assign";
|
|
case EXPR_SWIZZLE: return "swizzle";
|
|
case EXPR_TERNARY: return "ternary";
|
|
case EXPR_TEST_HOOK: return "test_hook";
|
|
case EXPR_TRY: return "try";
|
|
case EXPR_TRY_UNRESOLVED: return "try_unresolved";
|
|
case EXPR_TRY_UNWRAP_CHAIN: return "try_unwrap_chain";
|
|
case EXPR_TYPEID: return "typeid";
|
|
case EXPR_TYPEID_INFO: return "typeid_info";
|
|
case EXPR_TYPEINFO: return "typeinfo";
|
|
case EXPR_UNARY: return "unary";
|
|
case EXPR_VASPLAT: return "vasplat";
|
|
case EXPR_VECTOR_FROM_ARRAY: return "vector_from_array";
|
|
case EXPR_EXT_TRUNC: return "ext_trunc";
|
|
case EXPR_INT_TO_BOOL: return "int_to_bool";
|
|
default: return "???";
|
|
}
|
|
}
|
|
Expr *expr_negate_expr(Expr *expr)
|
|
{
|
|
if (expr->expr_kind == EXPR_UNARY && expr->unary_expr.operator == UNARYOP_NEG)
|
|
{
|
|
return expr->inner_expr;
|
|
}
|
|
Expr *neg = expr_new_expr(EXPR_UNARY, expr);
|
|
neg->unary_expr = (ExprUnary) { .operator = UNARYOP_NEG, .expr = expr };
|
|
return neg;
|
|
}
|
|
|
|
bool expr_in_int_range(Expr *expr, int64_t low, int64_t high)
|
|
{
|
|
ASSERT(expr_is_const(expr) && expr->const_expr.const_kind == CONST_INTEGER);
|
|
Int val = expr->const_expr.ixx;
|
|
if (!int_fits(val, TYPE_I64)) return false;
|
|
int64_t value = int_to_i64(val);
|
|
return value >= low && value <= high;
|
|
}
|
|
|
|
bool expr_is_zero(Expr *expr)
|
|
{
|
|
if (!sema_cast_const(expr)) return false;
|
|
switch (expr->const_expr.const_kind)
|
|
{
|
|
case CONST_FLOAT:
|
|
return !expr->const_expr.fxx.f;
|
|
case CONST_INTEGER:
|
|
return int_is_zero(expr->const_expr.ixx);
|
|
case CONST_BOOL:
|
|
return !expr->const_expr.b;
|
|
case CONST_ENUM:
|
|
case CONST_ERR:
|
|
return !expr->const_expr.enum_err_val->enum_constant.ordinal;
|
|
case CONST_BYTES:
|
|
case CONST_STRING:
|
|
{
|
|
size_t len = expr->const_expr.bytes.len;
|
|
for (size_t i = 0; i < len; i++)
|
|
{
|
|
if (expr->const_expr.bytes.ptr[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
case CONST_POINTER:
|
|
return !expr->const_expr.ptr;
|
|
case CONST_TYPEID:
|
|
return !expr->const_expr.typeid;
|
|
case CONST_SLICE:
|
|
return const_init_is_zero(expr->const_expr.slice_init);
|
|
case CONST_INITIALIZER:
|
|
return const_init_is_zero(expr->const_expr.initializer);
|
|
case CONST_UNTYPED_LIST:
|
|
{
|
|
FOREACH(Expr *, e, expr->const_expr.untyped_list)
|
|
{
|
|
if (!expr_is_zero(e)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
case CONST_REF:
|
|
return !expr->const_expr.global_ref;
|
|
case CONST_MEMBER:
|
|
return false;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
bool expr_is_unwrapped_ident(Expr *expr)
|
|
{
|
|
if (expr->expr_kind != EXPR_IDENTIFIER) return false;
|
|
Decl *decl = expr->ident_expr;
|
|
if (decl->decl_kind != DECL_VAR) return false;
|
|
return decl->var.kind == VARDECL_UNWRAPPED && IS_OPTIONAL(decl->var.alias);
|
|
}
|
|
|
|
bool expr_may_addr(Expr *expr)
|
|
{
|
|
if (IS_OPTIONAL(expr)) return false;
|
|
switch (expr->expr_kind)
|
|
{
|
|
case UNRESOLVED_EXPRS:
|
|
UNREACHABLE
|
|
case EXPR_IDENTIFIER:
|
|
{
|
|
Decl *decl = expr->ident_expr;
|
|
if (decl->decl_kind != DECL_VAR) return false;
|
|
decl = decl_raw(decl);
|
|
switch (decl->var.kind)
|
|
{
|
|
case VARDECL_LOCAL_CT:
|
|
case VARDECL_LOCAL_CT_TYPE:
|
|
case VARDECL_LOCAL:
|
|
case VARDECL_GLOBAL:
|
|
case VARDECL_PARAM:
|
|
case VARDECL_CONST:
|
|
return true;
|
|
case VARDECL_MEMBER:
|
|
case VARDECL_BITMEMBER:
|
|
case VARDECL_PARAM_CT:
|
|
case VARDECL_PARAM_CT_TYPE:
|
|
case VARDECL_PARAM_EXPR:
|
|
return false;
|
|
case VARDECL_UNWRAPPED:
|
|
case VARDECL_ERASE:
|
|
case VARDECL_REWRAPPED:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
case EXPR_UNARY:
|
|
return expr->unary_expr.operator == UNARYOP_DEREF;
|
|
case EXPR_BITACCESS:
|
|
case EXPR_ACCESS_RESOLVED:
|
|
return expr_may_addr(expr->access_resolved_expr.parent);
|
|
case EXPR_SUBSCRIPT:
|
|
case EXPR_SLICE:
|
|
case EXPR_MEMBER_GET:
|
|
return true;
|
|
case EXPR_BENCHMARK_HOOK:
|
|
case EXPR_TEST_HOOK:
|
|
case EXPR_VECTOR_FROM_ARRAY:
|
|
case EXPR_ANYFAULT_TO_FAULT:
|
|
case EXPR_VECTOR_TO_ARRAY:
|
|
case EXPR_SLICE_TO_VEC_ARRAY:
|
|
case EXPR_SCALAR_TO_VECTOR:
|
|
case EXPR_PTR_ACCESS:
|
|
case EXPR_ENUM_FROM_ORD:
|
|
case EXPR_FLOAT_TO_INT:
|
|
case EXPR_INT_TO_FLOAT:
|
|
case EXPR_INT_TO_PTR:
|
|
case EXPR_PTR_TO_INT:
|
|
case EXPR_SLICE_LEN:
|
|
case EXPR_RVALUE:
|
|
case EXPR_RECAST:
|
|
case EXPR_DISCARD:
|
|
case EXPR_ADDR_CONVERSION:
|
|
return false;
|
|
case NON_RUNTIME_EXPR:
|
|
case EXPR_ASM:
|
|
case EXPR_BINARY:
|
|
case EXPR_BITASSIGN:
|
|
case EXPR_BUILTIN:
|
|
case EXPR_BUILTIN_ACCESS:
|
|
case EXPR_CALL:
|
|
case EXPR_MAKE_ANY:
|
|
case EXPR_CATCH:
|
|
case EXPR_COND:
|
|
case EXPR_CONST:
|
|
case EXPR_DECL:
|
|
case EXPR_DEFAULT_ARG:
|
|
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
|
case EXPR_DESIGNATOR:
|
|
case EXPR_EXPRESSION_LIST:
|
|
case EXPR_FORCE_UNWRAP:
|
|
case EXPR_INITIALIZER_LIST:
|
|
case EXPR_LAMBDA:
|
|
case EXPR_LAST_FAULT:
|
|
case EXPR_MACRO_BLOCK:
|
|
case EXPR_MACRO_BODY_EXPANSION:
|
|
case EXPR_NAMED_ARGUMENT:
|
|
case EXPR_NOP:
|
|
case EXPR_OPERATOR_CHARS:
|
|
case EXPR_OPTIONAL:
|
|
case EXPR_POINTER_OFFSET:
|
|
case EXPR_POST_UNARY:
|
|
case EXPR_RETHROW:
|
|
case EXPR_RETVAL:
|
|
case EXPR_SLICE_ASSIGN:
|
|
case EXPR_SLICE_COPY:
|
|
case EXPR_SUBSCRIPT_ADDR:
|
|
case EXPR_SUBSCRIPT_ASSIGN:
|
|
case EXPR_SWIZZLE:
|
|
case EXPR_TERNARY:
|
|
case EXPR_TRY:
|
|
case EXPR_TRY_UNWRAP_CHAIN:
|
|
case EXPR_TYPEID_INFO:
|
|
case EXPR_EXT_TRUNC:
|
|
case EXPR_INT_TO_BOOL:
|
|
case EXPR_MAKE_SLICE:
|
|
return false;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
|
|
bool expr_is_runtime_const(Expr *expr)
|
|
{
|
|
ASSERT(expr->resolve_status == RESOLVE_DONE);
|
|
RETRY:
|
|
switch (expr->expr_kind)
|
|
{
|
|
case EXPR_POINTER_OFFSET:
|
|
return exprid_is_runtime_const(expr->pointer_offset_expr.ptr) && exprid_is_runtime_const(
|
|
expr->pointer_offset_expr.offset);
|
|
case EXPR_SWIZZLE:
|
|
case EXPR_RETVAL:
|
|
case EXPR_BUILTIN:
|
|
case EXPR_CT_EVAL:
|
|
case EXPR_BENCHMARK_HOOK:
|
|
case EXPR_TEST_HOOK:
|
|
case EXPR_BITASSIGN:
|
|
case EXPR_TYPECALL:
|
|
case EXPR_BINARY:
|
|
case EXPR_OPERATOR_CHARS:
|
|
case EXPR_STRINGIFY:
|
|
case EXPR_CT_CASTABLE:
|
|
case EXPR_CT_DEFINED:
|
|
case EXPR_CT_IS_CONST:
|
|
case EXPR_LAMBDA:
|
|
case EXPR_DECL:
|
|
case EXPR_CALL:
|
|
case EXPR_CATCH:
|
|
case EXPR_MACRO_BODY_EXPANSION:
|
|
case EXPR_TRY:
|
|
case EXPR_TRY_UNWRAP_CHAIN:
|
|
case EXPR_POST_UNARY:
|
|
case EXPR_SLICE_ASSIGN:
|
|
case EXPR_SLICE_COPY:
|
|
case EXPR_SPLAT:
|
|
case EXPR_MACRO_BLOCK:
|
|
case EXPR_RETHROW:
|
|
case EXPR_MEMBER_GET:
|
|
case EXPR_BITACCESS:
|
|
case EXPR_COND:
|
|
case EXPR_PTR_ACCESS:
|
|
case EXPR_INT_TO_FLOAT:
|
|
case EXPR_FLOAT_TO_INT:
|
|
case EXPR_SLICE_LEN:
|
|
return false;
|
|
case UNRESOLVED_EXPRS:
|
|
UNREACHABLE
|
|
case EXPR_VECTOR_FROM_ARRAY:
|
|
case EXPR_ANYFAULT_TO_FAULT:
|
|
case EXPR_RVALUE:
|
|
case EXPR_RECAST:
|
|
case EXPR_ADDR_CONVERSION:
|
|
case EXPR_DISCARD:
|
|
case EXPR_INT_TO_PTR:
|
|
case EXPR_PTR_TO_INT:
|
|
case EXPR_ENUM_FROM_ORD:
|
|
case EXPR_VECTOR_TO_ARRAY:
|
|
case EXPR_SLICE_TO_VEC_ARRAY:
|
|
case EXPR_SCALAR_TO_VECTOR:
|
|
return expr_is_runtime_const(expr->inner_expr);
|
|
case EXPR_MAKE_SLICE:
|
|
expr = expr->make_slice_expr.ptr;
|
|
if (!expr) return true;
|
|
goto RETRY;
|
|
case EXPR_MAKE_ANY:
|
|
if (!expr_is_runtime_const(expr->make_any_expr.typeid)) return false;
|
|
expr = expr->make_any_expr.inner;
|
|
goto RETRY;
|
|
case EXPR_ACCESS_RESOLVED:
|
|
expr = expr->access_resolved_expr.parent;
|
|
goto RETRY;
|
|
case EXPR_BUILTIN_ACCESS:
|
|
switch (expr->builtin_access_expr.kind)
|
|
{
|
|
case ACCESS_ENUMNAME:
|
|
case ACCESS_FAULTNAME:
|
|
case ACCESS_FAULTORDINAL:
|
|
break;
|
|
case ACCESS_TYPEOFANYFAULT:
|
|
case ACCESS_TYPEOFANY:
|
|
break;
|
|
}
|
|
return exprid_is_runtime_const(expr->builtin_access_expr.inner);
|
|
case EXPR_INT_TO_BOOL:
|
|
return expr_is_runtime_const(expr->int_to_bool_expr.inner);
|
|
case EXPR_EXT_TRUNC:
|
|
return expr_is_runtime_const(expr->ext_trunc_expr.inner);
|
|
case EXPR_CONST:
|
|
return true;
|
|
case EXPR_DESIGNATOR:
|
|
expr = expr->designator_expr.value;
|
|
if (!expr) return true;
|
|
goto RETRY;
|
|
case EXPR_IDENTIFIER:
|
|
{
|
|
Decl *ident = decl_flatten(expr->ident_expr);
|
|
if (ident->decl_kind != DECL_VAR) return true;
|
|
switch (ident->var.kind)
|
|
{
|
|
case VARDECL_CONST:
|
|
if (ident->is_extern) return false;
|
|
expr = ident->var.init_expr;
|
|
goto RETRY;
|
|
case VARDECL_PARAM_CT_TYPE:
|
|
case VARDECL_LOCAL_CT_TYPE:
|
|
case VARDECL_LOCAL_CT:
|
|
case VARDECL_PARAM_CT:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
case EXPR_EXPRESSION_LIST:
|
|
return expr_list_is_constant_eval(expr->expression_list);
|
|
case EXPR_TYPEID_INFO:
|
|
expr = exprptr(expr->typeid_info_expr.parent);
|
|
goto RETRY;
|
|
case EXPR_OPTIONAL:
|
|
expr = expr->inner_expr;
|
|
goto RETRY;
|
|
case EXPR_DEFAULT_ARG:
|
|
expr = expr->default_arg_expr.inner;
|
|
goto RETRY;
|
|
case EXPR_INITIALIZER_LIST:
|
|
return expr_list_is_constant_eval(expr->initializer_list);
|
|
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
|
return expr_list_is_constant_eval(expr->designated_init_list);
|
|
case EXPR_SLICE:
|
|
if (!exprid_is_runtime_const(expr->slice_expr.expr)) return false;
|
|
return expr->slice_expr.range.range_type == RANGE_CONST_RANGE;
|
|
case EXPR_SUBSCRIPT:
|
|
if (!exprid_is_runtime_const(expr->subscript_expr.index.expr)) return false;
|
|
expr = exprptr(expr->subscript_expr.expr);
|
|
goto RETRY;
|
|
case EXPR_SUBSCRIPT_ADDR:
|
|
if (!exprid_is_runtime_const(expr->subscript_expr.index.expr)) return false;
|
|
expr = exprptr(expr->subscript_expr.expr);
|
|
if (expr->expr_kind == EXPR_IDENTIFIER)
|
|
{
|
|
Decl *decl = expr->ident_expr;
|
|
if (decl->decl_kind == DECL_VAR)
|
|
{
|
|
switch (decl->var.kind)
|
|
{
|
|
case VARDECL_CONST:
|
|
case VARDECL_GLOBAL:
|
|
break;
|
|
case VARDECL_LOCAL:
|
|
if (decl->var.is_static) break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
goto RETRY;
|
|
case EXPR_TERNARY:
|
|
ASSERT(!exprid_is_runtime_const(expr->ternary_expr.cond));
|
|
return false;
|
|
case EXPR_FORCE_UNWRAP:
|
|
case EXPR_LAST_FAULT:
|
|
return false;
|
|
case EXPR_UNARY:
|
|
switch (expr->unary_expr.operator)
|
|
{
|
|
case UNARYOP_DEREF:
|
|
case UNARYOP_ERROR:
|
|
return false;
|
|
case UNARYOP_ADDR:
|
|
return expr_unary_addr_is_constant_eval(expr);
|
|
case UNARYOP_TADDR:
|
|
expr = expr->unary_expr.expr;
|
|
goto RETRY;
|
|
case UNARYOP_PLUS:
|
|
case UNARYOP_NEG:
|
|
case UNARYOP_BITNEG:
|
|
case UNARYOP_NOT:
|
|
expr = expr->unary_expr.expr;
|
|
goto RETRY;
|
|
case UNARYOP_INC:
|
|
case UNARYOP_DEC:
|
|
return false;
|
|
}
|
|
UNREACHABLE
|
|
case EXPR_COMPILER_CONST:
|
|
// Not foldable
|
|
return false;
|
|
case EXPR_CT_CALL:
|
|
case EXPR_TYPEINFO:
|
|
case EXPR_HASH_IDENT:
|
|
case EXPR_CT_IDENT:
|
|
case EXPR_POISONED:
|
|
case EXPR_CT_ARG:
|
|
case EXPR_ASM:
|
|
case EXPR_SUBSCRIPT_ASSIGN:
|
|
case EXPR_NAMED_ARGUMENT:
|
|
UNREACHABLE
|
|
case EXPR_NOP:
|
|
return true;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
|
|
static inline bool expr_list_is_constant_eval(Expr **exprs)
|
|
{
|
|
FOREACH(Expr *, expr, exprs)
|
|
{
|
|
if (!expr_is_runtime_const(expr)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static inline bool expr_unary_addr_is_constant_eval(Expr *expr)
|
|
{
|
|
// An address is never a constant value.
|
|
Expr *inner = expr->unary_expr.expr;
|
|
if (IS_OPTIONAL(inner)) return false;
|
|
switch (inner->expr_kind)
|
|
{
|
|
case UNRESOLVED_EXPRS:
|
|
UNREACHABLE
|
|
case EXPR_ACCESS_RESOLVED:
|
|
return expr_is_runtime_const(inner);
|
|
case EXPR_CONST:
|
|
case EXPR_INITIALIZER_LIST:
|
|
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
|
// We can't create temporaries as const locally or making them into compile time constants.
|
|
return expr_is_runtime_const(inner);
|
|
case EXPR_IDENTIFIER:
|
|
{
|
|
// The address of an identifier is side effect free.
|
|
Decl *decl = inner->ident_expr;
|
|
if (decl->decl_kind == DECL_FUNC) return true;
|
|
if (decl->decl_kind != DECL_VAR) return false;
|
|
switch (decl->var.kind)
|
|
{
|
|
case VARDECL_CONST:
|
|
case VARDECL_GLOBAL:
|
|
// Fine for both local and global init.
|
|
return true;
|
|
case VARDECL_LOCAL:
|
|
// Getting the address of a local can never be constant init unless it is static.
|
|
return decl->var.is_static;
|
|
case VARDECL_PARAM:
|
|
case VARDECL_MEMBER:
|
|
case VARDECL_BITMEMBER:
|
|
case VARDECL_PARAM_CT:
|
|
case VARDECL_PARAM_CT_TYPE:
|
|
case VARDECL_PARAM_EXPR:
|
|
case VARDECL_LOCAL_CT:
|
|
case VARDECL_LOCAL_CT_TYPE:
|
|
case VARDECL_UNWRAPPED:
|
|
case VARDECL_ERASE:
|
|
case VARDECL_REWRAPPED:
|
|
// None of these are constant.
|
|
return false;
|
|
}
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void expr_insert_addr(Expr *original)
|
|
{
|
|
ASSERT(original->resolve_status == RESOLVE_DONE);
|
|
if (original->expr_kind == EXPR_UNARY && original->unary_expr.operator == UNARYOP_DEREF)
|
|
{
|
|
*original = *original->unary_expr.expr;
|
|
return;
|
|
}
|
|
Expr *inner = expr_copy(original);
|
|
original->expr_kind = EXPR_UNARY;
|
|
Type *inner_type = inner->type;
|
|
bool optional = type_is_optional(inner->type);
|
|
original->type = type_add_optional(type_get_ptr(type_no_optional(inner_type)), optional);
|
|
original->unary_expr.operator = UNARYOP_ADDR;
|
|
original->unary_expr.expr = inner;
|
|
}
|
|
|
|
Expr *expr_generate_decl(Decl *decl, Expr *assign)
|
|
{
|
|
ASSERT(decl->decl_kind == DECL_VAR);
|
|
ASSERT(decl->var.init_expr == NULL);
|
|
Expr *expr_decl = expr_new(EXPR_DECL, decl->span);
|
|
expr_decl->decl_expr = decl;
|
|
if (!assign) decl->var.no_init = true;
|
|
decl->var.init_expr = assign;
|
|
return expr_decl;
|
|
}
|
|
|
|
static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, ArraySize index, bool from_back)
|
|
{
|
|
switch (initializer->kind)
|
|
{
|
|
case CONST_INIT_ZERO:
|
|
case CONST_INIT_STRUCT:
|
|
case CONST_INIT_UNION:
|
|
case CONST_INIT_VALUE:
|
|
return initializer;
|
|
case CONST_INIT_ARRAY_FULL:
|
|
{
|
|
unsigned len = vec_size(initializer->init_array_full);
|
|
if (from_back)
|
|
{
|
|
if (index > len || !index) return NULL;
|
|
index = len - index;
|
|
}
|
|
return initializer->init_array_full[index];
|
|
}
|
|
case CONST_INIT_ARRAY:
|
|
{
|
|
if (from_back)
|
|
{
|
|
ArraySize len = initializer->type->array.len;
|
|
if (index > len || !index) return NULL;
|
|
index = len - index;
|
|
}
|
|
FOREACH(ConstInitializer *, init, initializer->init_array.elements)
|
|
{
|
|
ASSERT(init->kind == CONST_INIT_ARRAY_VALUE);
|
|
if (init->init_array_value.index == index) return init->init_array_value.element;
|
|
}
|
|
return NULL;
|
|
}
|
|
case CONST_INIT_ARRAY_VALUE:
|
|
UNREACHABLE
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
void expr_rewrite_to_const_zero(Expr *expr, Type *type)
|
|
{
|
|
expr->expr_kind = EXPR_CONST;
|
|
Type *canonical = type->canonical;
|
|
switch (canonical->type_kind)
|
|
{
|
|
case TYPE_POISONED:
|
|
case TYPE_VOID:
|
|
case TYPE_INFERRED_VECTOR:
|
|
case TYPE_WILDCARD:
|
|
UNREACHABLE
|
|
case ALL_INTS:
|
|
expr_rewrite_const_int(expr, type, 0);
|
|
return;
|
|
case ALL_FLOATS:
|
|
expr_rewrite_const_float(expr, type, 0);
|
|
break;
|
|
case TYPE_BOOL:
|
|
expr_rewrite_const_bool(expr, type, false);
|
|
return;
|
|
case TYPE_POINTER:
|
|
case TYPE_FAULTTYPE:
|
|
case TYPE_ANY:
|
|
case TYPE_INTERFACE:
|
|
case TYPE_ANYFAULT:
|
|
case TYPE_TYPEID:
|
|
case TYPE_FUNC_PTR:
|
|
expr_rewrite_const_null(expr, type);
|
|
return;
|
|
case TYPE_ENUM:
|
|
expr->const_expr.const_kind = CONST_ENUM;
|
|
ASSERT(canonical->decl->resolve_status == RESOLVE_DONE);
|
|
expr->const_expr.enum_err_val = canonical->decl->enums.values[0];
|
|
expr->resolve_status = RESOLVE_DONE;
|
|
break;
|
|
case TYPE_FUNC_RAW:
|
|
case TYPE_TYPEDEF:
|
|
case TYPE_OPTIONAL:
|
|
case TYPE_TYPEINFO:
|
|
case TYPE_MEMBER:
|
|
case TYPE_UNTYPED_LIST:
|
|
case TYPE_INFERRED_ARRAY:
|
|
case TYPE_FLEXIBLE_ARRAY:
|
|
UNREACHABLE
|
|
case TYPE_SLICE:
|
|
expr_rewrite_const_empty_slice(expr, type);
|
|
return;
|
|
case TYPE_STRUCT:
|
|
case TYPE_UNION:
|
|
case TYPE_BITSTRUCT:
|
|
case TYPE_VECTOR:
|
|
case TYPE_ARRAY:
|
|
expr_rewrite_const_initializer(expr, type, const_init_new_zero(type));
|
|
return;
|
|
case TYPE_DISTINCT:
|
|
expr_rewrite_to_const_zero(expr, canonical->decl->distinct->type);
|
|
break;
|
|
}
|
|
expr->type = type;
|
|
}
|
|
|
|
Expr *expr_from_const_expr_at_index(Expr *expr, ArrayIndex index)
|
|
{
|
|
ExprConst *expr_const = &expr->const_expr;
|
|
switch (expr_const->const_kind)
|
|
{
|
|
case CONST_ERR:
|
|
case CONST_FLOAT:
|
|
case CONST_INTEGER:
|
|
case CONST_BOOL:
|
|
case CONST_ENUM:
|
|
case CONST_POINTER:
|
|
case CONST_TYPEID:
|
|
case CONST_REF:
|
|
case CONST_MEMBER:
|
|
UNREACHABLE
|
|
case CONST_BYTES:
|
|
case CONST_STRING:
|
|
{
|
|
uint8_t c = expr_const->bytes.ptr[index];
|
|
Type *indexed = type_get_indexed_type(expr->type);
|
|
return expr_new_const_int(expr->span, indexed, c);
|
|
}
|
|
case CONST_UNTYPED_LIST:
|
|
return copy_expr_single(expr_const->untyped_list[index]);
|
|
case CONST_SLICE:
|
|
{
|
|
Expr *val = expr_new_expr(EXPR_CONST, expr);
|
|
if (!expr_rewrite_to_const_initializer_index(expr->type, expr_const->slice_init, val, index, false)) return poisoned_expr;
|
|
return val;
|
|
}
|
|
case CONST_INITIALIZER:
|
|
{
|
|
Expr *val = expr_new_expr(EXPR_CONST, expr);
|
|
if (!expr_rewrite_to_const_initializer_index(expr->type, expr_const->initializer, val, index, false)) return poisoned_expr;
|
|
return val;
|
|
}
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
bool expr_rewrite_to_const_initializer_index(Type *list_type, ConstInitializer *list, Expr *result, unsigned index, bool from_back)
|
|
{
|
|
ConstInitializer *initializer = initializer_for_index(list, index, from_back);
|
|
ConstInitType kind = initializer ? initializer->kind : CONST_INIT_ZERO;
|
|
switch (kind)
|
|
{
|
|
case CONST_INIT_ZERO:
|
|
{
|
|
Type *indexed_type = type_get_indexed_type(list_type);
|
|
if (!indexed_type) return false;
|
|
expr_rewrite_to_const_zero(result, indexed_type);
|
|
return true;
|
|
}
|
|
case CONST_INIT_STRUCT:
|
|
case CONST_INIT_UNION:
|
|
case CONST_INIT_ARRAY:
|
|
case CONST_INIT_ARRAY_FULL:
|
|
case CONST_INIT_ARRAY_VALUE:
|
|
return false;
|
|
case CONST_INIT_VALUE:
|
|
assert(initializer);
|
|
expr_replace(result, initializer->init_value);
|
|
return true;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
// Determine if the expression has side effects
|
|
// Note! This is not the same as it being const.
|
|
bool expr_is_pure(Expr *expr)
|
|
{
|
|
if (!expr) return true;
|
|
switch (expr->expr_kind)
|
|
{
|
|
case UNRESOLVED_EXPRS:
|
|
UNREACHABLE
|
|
case EXPR_BUILTIN:
|
|
case EXPR_BENCHMARK_HOOK:
|
|
case EXPR_TEST_HOOK:
|
|
return false;
|
|
case EXPR_MAKE_SLICE:
|
|
return expr_is_pure(expr->make_slice_expr.ptr);
|
|
case EXPR_MAKE_ANY:
|
|
return expr_is_pure(expr->make_any_expr.inner) && expr_is_pure(expr->make_any_expr.typeid);
|
|
case EXPR_PTR_ACCESS:
|
|
case EXPR_VECTOR_TO_ARRAY:
|
|
case EXPR_SLICE_TO_VEC_ARRAY:
|
|
case EXPR_SCALAR_TO_VECTOR:
|
|
case EXPR_ENUM_FROM_ORD:
|
|
case EXPR_INT_TO_FLOAT:
|
|
case EXPR_INT_TO_PTR:
|
|
case EXPR_PTR_TO_INT:
|
|
case EXPR_FLOAT_TO_INT:
|
|
case EXPR_SLICE_LEN:
|
|
case EXPR_DISCARD:
|
|
case EXPR_VECTOR_FROM_ARRAY:
|
|
case EXPR_ANYFAULT_TO_FAULT:
|
|
case EXPR_RVALUE:
|
|
case EXPR_RECAST:
|
|
case EXPR_ADDR_CONVERSION:
|
|
return expr_is_pure(expr->inner_expr);
|
|
case EXPR_INT_TO_BOOL:
|
|
return expr_is_pure(expr->int_to_bool_expr.inner);
|
|
case EXPR_EXT_TRUNC:
|
|
return expr_is_pure(expr->ext_trunc_expr.inner);
|
|
case EXPR_SWIZZLE:
|
|
return exprid_is_pure(expr->swizzle_expr.parent);
|
|
case EXPR_BUILTIN_ACCESS:
|
|
return exprid_is_pure(expr->builtin_access_expr.inner);
|
|
case EXPR_POINTER_OFFSET:
|
|
return exprid_is_pure(expr->pointer_offset_expr.ptr) && exprid_is_pure(expr->pointer_offset_expr.offset);
|
|
case EXPR_COMPILER_CONST:
|
|
case EXPR_CONST:
|
|
case EXPR_TYPECALL:
|
|
case EXPR_CT_ARG:
|
|
case EXPR_CT_CALL:
|
|
case EXPR_CT_CASTABLE:
|
|
case EXPR_CT_DEFINED:
|
|
case EXPR_CT_IS_CONST:
|
|
case EXPR_CT_EVAL:
|
|
case EXPR_CT_IDENT:
|
|
case EXPR_IDENTIFIER:
|
|
case EXPR_LAMBDA:
|
|
case EXPR_NOP:
|
|
case EXPR_OPERATOR_CHARS:
|
|
case EXPR_RETVAL:
|
|
case EXPR_STRINGIFY:
|
|
case EXPR_TYPEINFO:
|
|
case EXPR_LAST_FAULT:
|
|
case EXPR_MEMBER_GET:
|
|
return true;
|
|
case EXPR_BITASSIGN:
|
|
return false;
|
|
case EXPR_BINARY:
|
|
// Anything with assignment is impure, otherwise true if sub expr are pure.
|
|
if (expr->binary_expr.operator >= BINARYOP_ASSIGN) return false;
|
|
return exprid_is_pure(expr->binary_expr.right) && exprid_is_pure(expr->binary_expr.left);
|
|
case EXPR_UNARY:
|
|
switch (expr->unary_expr.operator)
|
|
{
|
|
case UNARYOP_INC:
|
|
case UNARYOP_DEC:
|
|
case UNARYOP_TADDR:
|
|
// ++ -- &&1
|
|
return false;
|
|
case UNARYOP_ERROR:
|
|
case UNARYOP_DEREF:
|
|
case UNARYOP_ADDR:
|
|
case UNARYOP_NEG:
|
|
case UNARYOP_BITNEG:
|
|
case UNARYOP_NOT:
|
|
case UNARYOP_PLUS:
|
|
return expr_is_pure(expr->unary_expr.expr);
|
|
}
|
|
UNREACHABLE
|
|
case EXPR_BITACCESS:
|
|
case EXPR_ACCESS_RESOLVED:
|
|
// All access is pure if the parent is pure.
|
|
return expr_is_pure(expr->access_resolved_expr.parent);
|
|
case EXPR_POISONED:
|
|
UNREACHABLE
|
|
case EXPR_MACRO_BODY_EXPANSION:
|
|
case EXPR_CALL:
|
|
case EXPR_CATCH:
|
|
case EXPR_COND:
|
|
case EXPR_DESIGNATOR:
|
|
case EXPR_DECL:
|
|
case EXPR_OPTIONAL:
|
|
case EXPR_RETHROW:
|
|
case EXPR_HASH_IDENT:
|
|
case EXPR_MACRO_BLOCK:
|
|
case EXPR_NAMED_ARGUMENT:
|
|
case EXPR_INITIALIZER_LIST:
|
|
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
|
case EXPR_POST_UNARY:
|
|
case EXPR_SLICE_ASSIGN:
|
|
case EXPR_SLICE_COPY:
|
|
case EXPR_SPLAT:
|
|
case EXPR_TRY:
|
|
case EXPR_TRY_UNWRAP_CHAIN:
|
|
case EXPR_FORCE_UNWRAP:
|
|
case EXPR_SUBSCRIPT_ASSIGN:
|
|
return false;
|
|
case EXPR_EXPRESSION_LIST:
|
|
{
|
|
FOREACH(Expr *, e, expr->expression_list)
|
|
{
|
|
if (!expr_is_pure(e)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
case EXPR_TYPEID_INFO:
|
|
return exprid_is_pure(expr->typeid_info_expr.parent);
|
|
case EXPR_SLICE:
|
|
if (!exprid_is_pure(expr->slice_expr.expr)) return false;
|
|
switch (expr->slice_expr.range.range_type)
|
|
{
|
|
case RANGE_CONST_RANGE:
|
|
return true;
|
|
case RANGE_CONST_END:
|
|
case RANGE_CONST_LEN:
|
|
return exprid_is_pure(expr->slice_expr.range.start);
|
|
case RANGE_DYNAMIC:
|
|
return exprid_is_pure(expr->slice_expr.range.start)
|
|
&& exprid_is_pure(expr->slice_expr.range.end);
|
|
}
|
|
UNREACHABLE
|
|
case EXPR_SUBSCRIPT:
|
|
case EXPR_SUBSCRIPT_ADDR:
|
|
return exprid_is_pure(expr->subscript_expr.expr)
|
|
&& exprid_is_pure(expr->subscript_expr.index.expr);
|
|
case EXPR_TERNARY:
|
|
return exprid_is_pure(expr->ternary_expr.cond)
|
|
&& exprid_is_pure(expr->ternary_expr.else_expr)
|
|
&& exprid_is_pure(expr->ternary_expr.then_expr);
|
|
case EXPR_ASM:
|
|
return false;
|
|
case EXPR_DEFAULT_ARG:
|
|
return expr_is_pure(expr->default_arg_expr.inner);
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
|
|
bool expr_is_simple(Expr *expr, bool to_float)
|
|
{
|
|
RETRY:
|
|
switch (expr->expr_kind)
|
|
{
|
|
case EXPR_TERNARY:
|
|
return exprid_is_simple(expr->ternary_expr.else_expr, to_float) && exprid_is_simple(expr->ternary_expr.then_expr, to_float);
|
|
case EXPR_RETHROW:
|
|
expr = expr->rethrow_expr.inner;
|
|
goto RETRY;
|
|
default:
|
|
return true;
|
|
case EXPR_BINARY:
|
|
switch (expr->binary_expr.operator)
|
|
{
|
|
case BINARYOP_DIV:
|
|
if (to_float) return false;
|
|
FALLTHROUGH;
|
|
case BINARYOP_MOD:
|
|
case BINARYOP_ELSE:
|
|
return exprid_is_simple(expr->binary_expr.left, to_float) && exprid_is_simple(expr->binary_expr.right, to_float);
|
|
case BINARYOP_AND:
|
|
case BINARYOP_OR:
|
|
case BINARYOP_GT:
|
|
case BINARYOP_GE:
|
|
case BINARYOP_LT:
|
|
case BINARYOP_LE:
|
|
case BINARYOP_NE:
|
|
case BINARYOP_EQ:
|
|
case BINARYOP_VEC_GT:
|
|
case BINARYOP_VEC_GE:
|
|
case BINARYOP_VEC_LT:
|
|
case BINARYOP_VEC_LE:
|
|
case BINARYOP_VEC_NE:
|
|
case BINARYOP_VEC_EQ:
|
|
case BINARYOP_ASSIGN:
|
|
case BINARYOP_ADD_ASSIGN:
|
|
case BINARYOP_BIT_AND_ASSIGN:
|
|
case BINARYOP_BIT_OR_ASSIGN:
|
|
case BINARYOP_BIT_XOR_ASSIGN:
|
|
case BINARYOP_DIV_ASSIGN:
|
|
case BINARYOP_MOD_ASSIGN:
|
|
case BINARYOP_MULT_ASSIGN:
|
|
case BINARYOP_SHR_ASSIGN:
|
|
case BINARYOP_SHL_ASSIGN:
|
|
case BINARYOP_SUB_ASSIGN:
|
|
return true;
|
|
case BINARYOP_SHL:
|
|
case BINARYOP_SHR:
|
|
return to_float;
|
|
default:
|
|
return false;
|
|
}
|
|
UNREACHABLE
|
|
case EXPR_UNARY:
|
|
switch (expr->unary_expr.operator)
|
|
{
|
|
case UNARYOP_BITNEG:
|
|
return to_float;
|
|
case UNARYOP_NEG:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
|
|
Expr *expr_new(ExprKind kind, SourceSpan start)
|
|
{
|
|
Expr *expr = expr_calloc();
|
|
expr->expr_kind = kind;
|
|
expr->span = start;
|
|
return expr;
|
|
}
|
|
|
|
Expr *expr_new_const_int(SourceSpan span, Type *type, uint64_t v)
|
|
{
|
|
Expr *expr = expr_calloc();
|
|
expr->expr_kind = EXPR_CONST;
|
|
expr->span = span;
|
|
expr->type = type;
|
|
TypeKind kind = type_flatten(type)->type_kind;
|
|
expr->const_expr.ixx.i.high = 0;
|
|
if (type_kind_is_signed(kind))
|
|
{
|
|
if (v > (uint64_t)INT64_MAX) expr->const_expr.ixx.i.high = UINT64_MAX;
|
|
}
|
|
expr->const_expr.ixx.i.low = v;
|
|
expr->const_expr.ixx.type = kind;
|
|
expr->const_expr.is_character = false;
|
|
expr->const_expr.const_kind = CONST_INTEGER;
|
|
expr->resolve_status = RESOLVE_DONE;
|
|
return expr;
|
|
}
|
|
|
|
Expr *expr_new_const_typeid(SourceSpan span, Type *type)
|
|
{
|
|
Expr *expr = expr_calloc();
|
|
expr->expr_kind = EXPR_CONST;
|
|
expr->span = span;
|
|
expr->type = type_typeid;
|
|
expr->const_expr.const_kind = CONST_TYPEID;
|
|
expr->const_expr.typeid = type;
|
|
expr->resolve_status = RESOLVE_DONE;
|
|
return expr;
|
|
}
|
|
|
|
Expr *expr_new_const_initializer(SourceSpan span, Type *type, ConstInitializer *initializer)
|
|
{
|
|
Expr *expr = expr_calloc();
|
|
expr->expr_kind = EXPR_CONST;
|
|
expr->span = span;
|
|
expr->type = type;
|
|
expr->const_expr.initializer = initializer;
|
|
expr->const_expr.const_kind = CONST_INITIALIZER;
|
|
expr->resolve_status = RESOLVE_DONE;
|
|
return expr;
|
|
}
|
|
|
|
Expr *expr_new_const_bool(SourceSpan span, Type *type, bool value)
|
|
{
|
|
Expr *expr = expr_calloc();
|
|
expr->expr_kind = EXPR_CONST;
|
|
expr->span = span;
|
|
expr->type = type;
|
|
ASSERT(type_flatten(type)->type_kind == TYPE_BOOL);
|
|
expr->const_expr.b = value;
|
|
expr->const_expr.const_kind = CONST_BOOL;
|
|
expr->resolve_status = RESOLVE_DONE;
|
|
return expr;
|
|
}
|
|
|
|
Expr *expr_new_const_string(SourceSpan span, const char *string)
|
|
{
|
|
Expr *expr = expr_calloc();
|
|
expr->expr_kind = EXPR_CONST;
|
|
expr->span = span;
|
|
expr->type = type_string;
|
|
expr->const_expr.const_kind = CONST_STRING;
|
|
expr->const_expr.bytes.ptr = string;
|
|
expr->const_expr.bytes.len = strlen(string);
|
|
expr->resolve_status = RESOLVE_DONE;
|
|
return expr;
|
|
}
|
|
|
|
void expr_rewrite_to_builtin_access(Expr *expr, Expr *parent, BuiltinAccessKind kind, Type *type)
|
|
{
|
|
expr->expr_kind = EXPR_BUILTIN_ACCESS;
|
|
expr->builtin_access_expr.kind = kind;
|
|
expr->builtin_access_expr.inner = exprid(parent);
|
|
expr->type = type_add_optional(type, IS_OPTIONAL(parent));
|
|
expr->resolve_status = RESOLVE_DONE;
|
|
}
|
|
|
|
|
|
Expr *expr_variable(Decl *decl)
|
|
{
|
|
if (decl->resolve_status == RESOLVE_DONE)
|
|
{
|
|
Expr *expr = expr_new(EXPR_IDENTIFIER, decl->span);
|
|
expr_resolve_ident(expr, decl);
|
|
return expr;
|
|
}
|
|
Expr *expr = expr_new(EXPR_UNRESOLVED_IDENTIFIER, decl->span);
|
|
expr->unresolved_ident_expr = (ExprUnresolvedIdentifier) { .ident = decl->name };
|
|
expr->resolve_status = RESOLVE_NOT_DONE;
|
|
return expr;
|
|
}
|
|
|
|
void expr_rewrite_insert_deref(Expr *original)
|
|
{
|
|
// Assume *(&x) => x
|
|
if (original->expr_kind == EXPR_UNARY && original->unary_expr.operator == UNARYOP_ADDR)
|
|
{
|
|
*original = *original->unary_expr.expr;
|
|
return;
|
|
}
|
|
|
|
// Allocate our new and create our new inner, and overwrite the original.
|
|
Expr *inner = expr_copy(original);
|
|
original->expr_kind = EXPR_UNARY;
|
|
original->type = NULL;
|
|
original->unary_expr.operator = UNARYOP_DEREF;
|
|
original->unary_expr.expr = inner;
|
|
|
|
// In the case the original is already resolved, we want to resolve the deref as well.
|
|
if (original->resolve_status == RESOLVE_DONE)
|
|
{
|
|
Type *no_fail = type_no_optional(inner->type);
|
|
ASSERT(no_fail->canonical->type_kind == TYPE_POINTER);
|
|
|
|
// Only fold to the canonical type if it wasn't a pointer.
|
|
Type *pointee = no_fail->type_kind == TYPE_POINTER ? no_fail->pointer : no_fail->canonical->pointer;
|
|
original->type = type_add_optional(pointee, IS_OPTIONAL(inner));
|
|
}
|
|
}
|
|
|
|
void expr_rewrite_const_ref(Expr *expr_to_rewrite, Decl *decl)
|
|
{
|
|
expr_to_rewrite->const_expr = (ExprConst) {
|
|
.global_ref = decl,
|
|
.const_kind = CONST_REF
|
|
};
|
|
expr_to_rewrite->expr_kind = EXPR_CONST;
|
|
}
|
|
|
|
void expr_rewrite_const_string(Expr *expr_to_rewrite, const char *string)
|
|
{
|
|
expr_to_rewrite->expr_kind = EXPR_CONST;
|
|
expr_to_rewrite->const_expr.const_kind = CONST_STRING;
|
|
expr_to_rewrite->const_expr.bytes.ptr = (char *)string;
|
|
ArraySize len = (ArraySize)strlen(string);
|
|
expr_to_rewrite->const_expr.bytes.len = len;
|
|
expr_to_rewrite->resolve_status = RESOLVE_DONE;
|
|
expr_to_rewrite->type = type_string;
|
|
}
|
|
|
|
void expr_rewrite_to_binary(Expr *expr_to_rewrite, Expr *left, Expr *right, BinaryOp op)
|
|
{
|
|
expr_to_rewrite->binary_expr = (ExprBinary) { .operator = op, .left = exprid(left), .right = exprid(right) };
|
|
expr_to_rewrite->expr_kind = EXPR_BINARY;
|
|
}
|