+ case EXPR_FUNCNAME:
+ case EXPR_LABEL_ADDRESS:
+ return EXPR_CLASS_CONSTANT;
+
+ case EXPR_COMPOUND_LITERAL:
+ return is_constant_initializer(expression->compound_literal.initializer);
+
+ 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_linker_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_type_size(type_void_ptr)
+ ))
+ return is_constant_expression(expression);
+
+ return is_linker_constant(expression->unary.value);
+ }
+
+ case EXPR_BINARY_ADD:
+ case EXPR_BINARY_SUB: {
+ expression_t *const left = expression->binary.left;
+ expression_t *const right = expression->binary.right;
+ type_t *const ltype = skip_typeref(left->base.type);
+ type_t *const rtype = skip_typeref(right->base.type);
+
+ if (is_type_pointer(ltype)) {
+ expression_classification_t const l = is_linker_constant(left);
+ expression_classification_t const r = is_constant_expression(right);
+ return l < r ? l : r;
+ } else if (is_type_pointer(rtype)) {
+ expression_classification_t const l = is_constant_expression(left);
+ expression_classification_t const r = is_linker_constant(right);
+ return l < r ? l : r;
+ } else if (!is_type_valid(ltype) || !is_type_valid(rtype)) {
+ return EXPR_CLASS_ERROR;
+ } else {
+ return is_constant_expression(expression);
+ }
+ }
+
+ case EXPR_REFERENCE: {
+ entity_t *entity = expression->reference.entity;
+ if (!is_declaration(entity))
+ return EXPR_CLASS_VARIABLE;
+
+ type_t *type = skip_typeref(entity->declaration.type);
+ if (is_type_function(type))
+ return EXPR_CLASS_CONSTANT;
+ if (is_type_array(type)) {
+ return is_object_with_linker_constant_address(expression);
+ }
+ /* Prevent stray errors */
+ if (!is_type_valid(type))
+ return EXPR_CLASS_ERROR;
+ return EXPR_CLASS_VARIABLE;
+ }
+
+ case EXPR_ARRAY_ACCESS: {
+ type_t *const type =
+ skip_typeref(revert_automatic_type_conversion(expression));
+ if (!is_type_array(type))
+ return EXPR_CLASS_VARIABLE;
+ return is_linker_constant(expression->array_access.array_ref);
+ }
+
+ case EXPR_CONDITIONAL: {
+ expression_t *const c = expression->conditional.condition;
+ expression_classification_t const cclass = is_constant_expression(c);
+ if (cclass != EXPR_CLASS_CONSTANT)
+ return cclass;
+
+ if (fold_constant_to_bool(c)) {
+ expression_t const *const t = expression->conditional.true_expression;
+ return is_linker_constant(t != NULL ? t : c);
+ } else {
+ return is_linker_constant(expression->conditional.false_expression);
+ }
+ }
+
+ case EXPR_SELECT: {
+ entity_t *entity = expression->select.compound_entry;
+ if (!is_declaration(entity))
+ return EXPR_CLASS_VARIABLE;
+ type_t *type = skip_typeref(entity->declaration.type);
+ if (is_type_array(type)) {
+ /* arrays automatically convert to their address */
+ expression_t *compound = expression->select.compound;
+ type_t *base_type = skip_typeref(compound->base.type);
+ if (is_type_pointer(base_type)) {
+ /* it's a -> */
+ return is_linker_constant(compound);
+ } else {
+ return is_object_with_linker_constant_address(compound);
+ }
+ }
+ return EXPR_CLASS_VARIABLE;
+ }
+
+ default:
+ return is_constant_expression(expression);
+ }
+}
+
+/**
+ * Check if the given expression is a call to a builtin function
+ * returning a constant result.
+ */
+static expression_classification_t is_builtin_const_call(const expression_t *expression)
+{
+ expression_t *function = expression->call.function;
+ if (function->kind != EXPR_REFERENCE)
+ return EXPR_CLASS_VARIABLE;
+ reference_expression_t *ref = &function->reference;
+ if (ref->entity->kind != ENTITY_FUNCTION)
+ return EXPR_CLASS_VARIABLE;
+
+ switch (ref->entity->function.btk) {
+ case BUILTIN_INF:
+ case BUILTIN_NAN:
+ return EXPR_CLASS_CONSTANT;
+ default:
+ return EXPR_CLASS_VARIABLE;
+ }
+
+}
+
+static expression_classification_t is_constant_pointer(const expression_t *expression)
+{
+ expression_classification_t const expr_class = is_constant_expression(expression);
+ if (expr_class != EXPR_CLASS_VARIABLE)
+ return expr_class;
+
+ switch (expression->kind) {
+ case EXPR_UNARY_CAST:
+ return is_constant_pointer(expression->unary.value);
+ default:
+ return EXPR_CLASS_VARIABLE;
+ }
+}
+
+static expression_classification_t 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;
+ expression_classification_t const idx_class = is_constant_expression(array_access->index);
+ if (idx_class != EXPR_CLASS_CONSTANT)
+ return idx_class;
+ expression_classification_t const ref_addr = is_object_with_constant_address(array_access->array_ref);
+ expression_classification_t const ref_ptr = is_constant_pointer(array_access->array_ref);
+ return ref_addr > ref_ptr ? ref_addr : ref_ptr;
+ }
+
+ case EXPR_UNARY_DEREFERENCE:
+ return is_constant_pointer(expression->unary.value);
+
+ case EXPR_ERROR:
+ return EXPR_CLASS_ERROR;
+
+ default:
+ return EXPR_CLASS_VARIABLE;
+ }
+}
+
+expression_classification_t is_constant_expression(const expression_t *expression)
+{
+ switch (expression->kind) {
+ case EXPR_LITERAL_CASES:
+ case EXPR_LITERAL_CHARACTER: