+bool is_constant_initializer(const initializer_t *initializer)
+{
+ switch (initializer->kind) {
+ case INITIALIZER_STRING:
+ case INITIALIZER_WIDE_STRING:
+ case INITIALIZER_DESIGNATOR:
+ return true;
+
+ case INITIALIZER_VALUE:
+ return is_constant_expression(initializer->value.value);
+
+ case INITIALIZER_LIST:
+ for (size_t i = 0; i < initializer->list.len; ++i) {
+ initializer_t *sub_initializer = initializer->list.initializers[i];
+ if (!is_constant_initializer(sub_initializer))
+ return false;
+ }
+ return true;
+ }
+ panic("invalid initializer kind found");
+}
+
+static bool is_object_with_linker_constant_address(const expression_t *expression)
+{
+ switch (expression->kind) {
+ case EXPR_UNARY_DEREFERENCE:
+ return is_address_constant(expression->unary.value);
+
+ case EXPR_SELECT: {
+ type_t *base_type = skip_typeref(expression->select.compound->base.type);
+ if (is_type_pointer(base_type)) {
+ /* it's a -> */
+ return is_address_constant(expression->select.compound);
+ } else {
+ return is_object_with_linker_constant_address(expression->select.compound);
+ }
+ }
+
+ case EXPR_ARRAY_ACCESS:
+ return is_constant_expression(expression->array_access.index)
+ && is_address_constant(expression->array_access.array_ref);
+
+ case EXPR_REFERENCE: {
+ entity_t *entity = expression->reference.entity;
+ if (is_declaration(entity)) {
+ switch ((storage_class_tag_t)entity->declaration.storage_class) {
+ case STORAGE_CLASS_NONE:
+ case STORAGE_CLASS_EXTERN:
+ case STORAGE_CLASS_STATIC:
+ return
+ entity->kind != ENTITY_VARIABLE ||
+ !entity->variable.thread_local;
+
+ case STORAGE_CLASS_REGISTER:
+ case STORAGE_CLASS_TYPEDEF:
+ case STORAGE_CLASS_AUTO:
+ break;
+ }
+ }
+ return false;
+ }
+
+ default:
+ return false;
+ }
+}
+
+bool is_address_constant(const expression_t *expression)
+{
+ switch (expression->kind) {
+ case EXPR_UNARY_TAKE_ADDRESS:
+ return is_object_with_linker_constant_address(expression->unary.value);
+
+ case EXPR_UNARY_DEREFERENCE: {
+ type_t *real_type
+ = revert_automatic_type_conversion(expression->unary.value);
+ /* dereferencing a function is a NOP */
+ if (is_type_function(real_type)) {
+ return is_address_constant(expression->unary.value);
+ }
+ /* FALLTHROUGH */
+ }
+
+ case EXPR_UNARY_CAST: {
+ type_t *dest = skip_typeref(expression->base.type);
+ if (!is_type_pointer(dest) && (
+ dest->kind != TYPE_ATOMIC ||
+ !(get_atomic_type_flags(dest->atomic.akind) & ATOMIC_TYPE_FLAG_INTEGER) ||
+ get_atomic_type_size(dest->atomic.akind) < get_atomic_type_size(get_intptr_kind())
+ ))
+ return false;
+
+ return (is_constant_expression(expression->unary.value)
+ || is_address_constant(expression->unary.value));
+ }
+
+ case EXPR_BINARY_ADD:
+ case EXPR_BINARY_SUB: {
+ expression_t *left = expression->binary.left;
+ expression_t *right = expression->binary.right;
+
+ if (is_type_pointer(skip_typeref(left->base.type))) {
+ return is_address_constant(left) && is_constant_expression(right);
+ } else if (is_type_pointer(skip_typeref(right->base.type))) {
+ return is_constant_expression(left) && is_address_constant(right);
+ }
+
+ return false;
+ }
+
+ case EXPR_REFERENCE: {
+ entity_t *entity = expression->reference.entity;
+ if (!is_declaration(entity))
+ return false;
+
+ type_t *type = skip_typeref(entity->declaration.type);
+ if (is_type_function(type))
+ return true;
+ if (is_type_array(type)) {
+ return is_object_with_linker_constant_address(expression);
+ }
+ /* Prevent stray errors */
+ if (!is_type_valid(type))
+ return true;
+ return false;
+ }
+
+ case EXPR_ARRAY_ACCESS: {
+ type_t *const type =
+ skip_typeref(revert_automatic_type_conversion(expression));
+ return
+ is_type_array(type) &&
+ is_constant_expression(expression->array_access.index) &&
+ is_address_constant(expression->array_access.array_ref);
+ }
+
+ default:
+ return false;
+ }
+}
+
+static bool is_builtin_const_call(const expression_t *expression)
+{
+ expression_t *function = expression->call.function;
+ if (function->kind != EXPR_BUILTIN_SYMBOL) {
+ return false;
+ }
+
+ symbol_t *symbol = function->builtin_symbol.symbol;
+
+ switch (symbol->ID) {
+ case T___builtin_huge_val:
+ case T___builtin_inf:
+ case T___builtin_inff:
+ case T___builtin_infl:
+ case T___builtin_nan:
+ case T___builtin_nanf:
+ case T___builtin_nanl:
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_constant_pointer(const expression_t *expression)
+{
+ if (is_constant_expression(expression))
+ return true;
+
+ switch (expression->kind) {
+ case EXPR_UNARY_CAST:
+ return is_constant_pointer(expression->unary.value);
+ default:
+ return false;
+ }
+}
+
+static bool is_object_with_constant_address(const expression_t *expression)
+{
+ switch (expression->kind) {
+ case EXPR_SELECT: {
+ expression_t *compound = expression->select.compound;
+ type_t *compound_type = compound->base.type;
+ compound_type = skip_typeref(compound_type);
+ if (is_type_pointer(compound_type)) {
+ return is_constant_pointer(compound);
+ } else {
+ return is_object_with_constant_address(compound);
+ }
+ }
+
+ case EXPR_ARRAY_ACCESS: {
+ array_access_expression_t const* const array_access =
+ &expression->array_access;
+ return
+ is_constant_expression(array_access->index) && (
+ is_object_with_constant_address(array_access->array_ref) ||
+ is_constant_pointer(array_access->array_ref)
+ );
+ }
+
+ case EXPR_UNARY_DEREFERENCE:
+ return is_constant_pointer(expression->unary.value);
+ default:
+ return false;
+ }
+}
+
+bool is_constant_expression(const expression_t *expression)
+{
+ switch (expression->kind) {
+
+ case EXPR_CONST:
+ case EXPR_CHARACTER_CONSTANT:
+ case EXPR_WIDE_CHARACTER_CONSTANT:
+ case EXPR_STRING_LITERAL:
+ case EXPR_WIDE_STRING_LITERAL:
+ case EXPR_CLASSIFY_TYPE:
+ case EXPR_FUNCNAME:
+ case EXPR_OFFSETOF:
+ case EXPR_ALIGNOF:
+ case EXPR_BUILTIN_CONSTANT_P:
+ case EXPR_LABEL_ADDRESS:
+ case EXPR_REFERENCE_ENUM_VALUE:
+ return true;
+
+ case EXPR_SIZEOF: {
+ type_t *type = expression->typeprop.type;
+ if (type == NULL)
+ type = expression->typeprop.tp_expression->base.type;
+
+ type = skip_typeref(type);
+ if (is_type_array(type) && type->array.is_vla)
+ return false;
+ return true;
+ }
+
+ case EXPR_BUILTIN_SYMBOL:
+ case EXPR_BUILTIN_PREFETCH:
+ case EXPR_SELECT:
+ case EXPR_VA_START:
+ case EXPR_VA_ARG:
+ case EXPR_STATEMENT:
+ case EXPR_REFERENCE:
+ case EXPR_UNARY_POSTFIX_INCREMENT:
+ case EXPR_UNARY_POSTFIX_DECREMENT:
+ case EXPR_UNARY_PREFIX_INCREMENT:
+ case EXPR_UNARY_PREFIX_DECREMENT:
+ case EXPR_UNARY_ASSUME: /* has VOID type */
+ case EXPR_UNARY_DEREFERENCE:
+ case EXPR_UNARY_DELETE:
+ case EXPR_UNARY_DELETE_ARRAY:
+ case EXPR_UNARY_THROW:
+ case EXPR_BINARY_ASSIGN:
+ case EXPR_BINARY_MUL_ASSIGN:
+ case EXPR_BINARY_DIV_ASSIGN:
+ case EXPR_BINARY_MOD_ASSIGN:
+ case EXPR_BINARY_ADD_ASSIGN:
+ case EXPR_BINARY_SUB_ASSIGN:
+ case EXPR_BINARY_SHIFTLEFT_ASSIGN:
+ case EXPR_BINARY_SHIFTRIGHT_ASSIGN:
+ case EXPR_BINARY_BITWISE_AND_ASSIGN:
+ case EXPR_BINARY_BITWISE_XOR_ASSIGN:
+ case EXPR_BINARY_BITWISE_OR_ASSIGN:
+ case EXPR_BINARY_COMMA:
+ case EXPR_ARRAY_ACCESS:
+ return false;
+
+ case EXPR_UNARY_TAKE_ADDRESS:
+ return is_object_with_constant_address(expression->unary.value);
+
+ case EXPR_CALL:
+ return is_builtin_const_call(expression);
+
+ case EXPR_UNARY_NEGATE:
+ case EXPR_UNARY_PLUS:
+ case EXPR_UNARY_BITWISE_NEGATE:
+ case EXPR_UNARY_NOT:
+ return is_constant_expression(expression->unary.value);
+
+ case EXPR_UNARY_CAST:
+ case EXPR_UNARY_CAST_IMPLICIT:
+ return is_type_arithmetic(skip_typeref(expression->base.type))
+ && is_constant_expression(expression->unary.value);
+
+ case EXPR_BINARY_ADD:
+ case EXPR_BINARY_SUB:
+ case EXPR_BINARY_MUL:
+ case EXPR_BINARY_DIV:
+ case EXPR_BINARY_MOD:
+ case EXPR_BINARY_EQUAL:
+ case EXPR_BINARY_NOTEQUAL:
+ case EXPR_BINARY_LESS:
+ case EXPR_BINARY_LESSEQUAL:
+ case EXPR_BINARY_GREATER:
+ case EXPR_BINARY_GREATEREQUAL:
+ case EXPR_BINARY_BITWISE_AND:
+ case EXPR_BINARY_BITWISE_OR:
+ case EXPR_BINARY_BITWISE_XOR:
+ case EXPR_BINARY_SHIFTLEFT:
+ case EXPR_BINARY_SHIFTRIGHT:
+ case EXPR_BINARY_ISGREATER:
+ case EXPR_BINARY_ISGREATEREQUAL:
+ case EXPR_BINARY_ISLESS:
+ case EXPR_BINARY_ISLESSEQUAL:
+ case EXPR_BINARY_ISLESSGREATER:
+ case EXPR_BINARY_ISUNORDERED:
+ return is_constant_expression(expression->binary.left)
+ && is_constant_expression(expression->binary.right);
+
+ case EXPR_BINARY_LOGICAL_AND: {
+ expression_t const *const left = expression->binary.left;
+ if (!is_constant_expression(left))
+ return false;
+ if (fold_constant(left) == 0)
+ return true;
+ return is_constant_expression(expression->binary.right);
+ }
+
+ case EXPR_BINARY_LOGICAL_OR: {
+ expression_t const *const left = expression->binary.left;
+ if (!is_constant_expression(left))
+ return false;
+ if (fold_constant(left) != 0)
+ return true;
+ return is_constant_expression(expression->binary.right);
+ }
+
+ case EXPR_COMPOUND_LITERAL:
+ return is_constant_initializer(expression->compound_literal.initializer);
+
+ case EXPR_CONDITIONAL: {
+ expression_t *condition = expression->conditional.condition;
+ if (!is_constant_expression(condition))
+ return false;
+
+ long val = fold_constant(condition);
+ if (val != 0)
+ return is_constant_expression(expression->conditional.true_expression);
+ else
+ return is_constant_expression(expression->conditional.false_expression);
+ }
+
+ case EXPR_INVALID:
+ return true;
+
+ case EXPR_UNKNOWN:
+ break;
+ }
+ panic("invalid expression found (is constant expression)");
+}
+
+/**
+ * Initialize the AST construction.
+ */