+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: {
+ declaration_t *declaration = expression->reference.declaration;
+ switch((storage_class_tag_t) declaration->storage_class) {
+ case STORAGE_CLASS_NONE:
+ case STORAGE_CLASS_EXTERN:
+ case STORAGE_CLASS_STATIC:
+ return true;
+ default:
+ 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: {
+ declaration_t *declaration = expression->reference.declaration;
+ type_t *type = skip_typeref(declaration->type);
+ if(is_type_function(type))
+ return true;
+ if(is_type_array(type)) {
+ return is_object_with_linker_constant_address(expression);
+ }
+ return false;
+ }
+
+ 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_nan:
+ case T___builtin_nanf:
+ case T___builtin_nand:
+ 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_SELECT:
+ return is_constant_pointer(expression->select.compound);
+ 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:
+ return is_constant_pointer(expression->array_access.array_ref)
+ && is_constant_expression(expression->array_access.index);
+ case EXPR_UNARY_DEREFERENCE:
+ return is_constant_pointer(expression->unary.value);
+ default:
+ return false;
+ }
+}
+