+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 true;
+
+ case STORAGE_CLASS_REGISTER:
+ case STORAGE_CLASS_TYPEDEF:
+ case STORAGE_CLASS_AUTO:
+ case STORAGE_CLASS_THREAD:
+ case STORAGE_CLASS_THREAD_EXTERN:
+ case STORAGE_CLASS_THREAD_STATIC:
+ 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;
+ }
+}
+