+/**
+ * Mark declarations, which are read. This is used to detect variables, which
+ * are never read.
+ * Example:
+ * x = x + 1;
+ * x is not marked as "read", because it is only read to calculate its own new
+ * value.
+ *
+ * x += y; y += x;
+ * x and y are not detected as "not read", because multiple variables are
+ * involved.
+ */
+static void mark_vars_read(expression_t *const expr, entity_t *lhs_ent)
+{
+ switch (expr->kind) {
+ case EXPR_REFERENCE: {
+ entity_t *const entity = expr->reference.entity;
+ if (entity->kind != ENTITY_VARIABLE
+ && entity->kind != ENTITY_PARAMETER)
+ return;
+
+ if (lhs_ent != entity && lhs_ent != ENT_ANY) {
+ if (entity->kind == ENTITY_VARIABLE) {
+ entity->variable.read = true;
+ } else {
+ entity->parameter.read = true;
+ }
+ }
+ return;
+ }
+
+ case EXPR_CALL:
+ // TODO respect pure/const
+ mark_vars_read(expr->call.function, NULL);
+ for (call_argument_t *arg = expr->call.arguments; arg != NULL; arg = arg->next) {
+ mark_vars_read(arg->expression, NULL);
+ }
+ return;
+
+ case EXPR_CONDITIONAL:
+ // TODO lhs_decl should depend on whether true/false have an effect
+ mark_vars_read(expr->conditional.condition, NULL);
+ if (expr->conditional.true_expression != NULL)
+ mark_vars_read(expr->conditional.true_expression, lhs_ent);
+ mark_vars_read(expr->conditional.false_expression, lhs_ent);
+ return;
+
+ case EXPR_SELECT:
+ if (lhs_ent == ENT_ANY
+ && !is_type_compound(skip_typeref(expr->base.type)))
+ lhs_ent = NULL;
+ mark_vars_read(expr->select.compound, lhs_ent);
+ return;
+
+ case EXPR_ARRAY_ACCESS: {
+ expression_t *const ref = expr->array_access.array_ref;
+ mark_vars_read(ref, lhs_ent);
+ lhs_ent = determine_lhs_ent(ref, lhs_ent);
+ mark_vars_read(expr->array_access.index, lhs_ent);
+ return;
+ }
+
+ case EXPR_VA_ARG:
+ mark_vars_read(expr->va_arge.ap, lhs_ent);
+ return;
+
+ case EXPR_UNARY_CAST:
+ /* Special case: Use void cast to mark a variable as "read" */
+ if (is_type_atomic(skip_typeref(expr->base.type), ATOMIC_TYPE_VOID))
+ lhs_ent = NULL;
+ goto unary;
+
+
+ case EXPR_UNARY_THROW:
+ if (expr->unary.value == NULL)
+ return;
+ /* FALLTHROUGH */
+ case EXPR_UNARY_DEREFERENCE:
+ case EXPR_UNARY_DELETE:
+ case EXPR_UNARY_DELETE_ARRAY:
+ if (lhs_ent == ENT_ANY)
+ lhs_ent = NULL;
+ goto unary;
+
+ case EXPR_UNARY_NEGATE:
+ case EXPR_UNARY_PLUS:
+ case EXPR_UNARY_BITWISE_NEGATE:
+ case EXPR_UNARY_NOT:
+ case EXPR_UNARY_TAKE_ADDRESS:
+ case EXPR_UNARY_POSTFIX_INCREMENT:
+ case EXPR_UNARY_POSTFIX_DECREMENT:
+ case EXPR_UNARY_PREFIX_INCREMENT:
+ case EXPR_UNARY_PREFIX_DECREMENT:
+ case EXPR_UNARY_CAST_IMPLICIT:
+ case EXPR_UNARY_ASSUME:
+unary:
+ mark_vars_read(expr->unary.value, lhs_ent);
+ return;
+
+ 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_LOGICAL_AND:
+ case EXPR_BINARY_LOGICAL_OR:
+ case EXPR_BINARY_SHIFTLEFT:
+ case EXPR_BINARY_SHIFTRIGHT:
+ case EXPR_BINARY_COMMA:
+ case EXPR_BINARY_ISGREATER:
+ case EXPR_BINARY_ISGREATEREQUAL:
+ case EXPR_BINARY_ISLESS:
+ case EXPR_BINARY_ISLESSEQUAL:
+ case EXPR_BINARY_ISLESSGREATER:
+ case EXPR_BINARY_ISUNORDERED:
+ mark_vars_read(expr->binary.left, lhs_ent);
+ mark_vars_read(expr->binary.right, lhs_ent);
+ return;
+
+ 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: {
+ if (lhs_ent == ENT_ANY)
+ lhs_ent = NULL;
+ lhs_ent = determine_lhs_ent(expr->binary.left, lhs_ent);
+ mark_vars_read(expr->binary.right, lhs_ent);
+ return;
+ }
+
+ case EXPR_VA_START:
+ determine_lhs_ent(expr->va_starte.ap, lhs_ent);
+ return;
+
+ case EXPR_UNKNOWN:
+ case EXPR_INVALID:
+ case EXPR_CONST:
+ case EXPR_CHARACTER_CONSTANT:
+ case EXPR_WIDE_CHARACTER_CONSTANT:
+ case EXPR_STRING_LITERAL:
+ case EXPR_WIDE_STRING_LITERAL:
+ case EXPR_COMPOUND_LITERAL: // TODO init?
+ case EXPR_SIZEOF:
+ case EXPR_CLASSIFY_TYPE:
+ case EXPR_ALIGNOF:
+ case EXPR_FUNCNAME:
+ case EXPR_BUILTIN_SYMBOL:
+ case EXPR_BUILTIN_CONSTANT_P:
+ case EXPR_BUILTIN_PREFETCH:
+ case EXPR_OFFSETOF:
+ case EXPR_STATEMENT: // TODO
+ case EXPR_LABEL_ADDRESS:
+ case EXPR_REFERENCE_ENUM_VALUE:
+ return;
+ }
+
+ panic("unhandled expression");
+}
+
+static designator_t *parse_designation(void)
+{
+ designator_t *result = NULL;
+ designator_t *last = NULL;
+
+ while (true) {
+ designator_t *designator;
+ switch (token.type) {
+ case '[':
+ designator = allocate_ast_zero(sizeof(designator[0]));
+ designator->source_position = token.source_position;
+ next_token();
+ add_anchor_token(']');
+ designator->array_index = parse_constant_expression();
+ rem_anchor_token(']');
+ expect(']', end_error);
+ break;
+ case '.':
+ designator = allocate_ast_zero(sizeof(designator[0]));
+ designator->source_position = token.source_position;
+ next_token();
+ if (token.type != T_IDENTIFIER) {
+ parse_error_expected("while parsing designator",
+ T_IDENTIFIER, NULL);
+ return NULL;
+ }
+ designator->symbol = token.v.symbol;
+ next_token();
+ break;
+ default:
+ expect('=', end_error);
+ return result;
+ }
+
+ assert(designator != NULL);
+ if (last != NULL) {
+ last->next = designator;
+ } else {
+ result = designator;
+ }
+ last = designator;
+ }
+end_error:
+ return NULL;
+}
+
+static initializer_t *initializer_from_string(array_type_t *type,
+ const string_t *const string)
+{
+ /* TODO: check len vs. size of array type */
+ (void) type;
+
+ initializer_t *initializer = allocate_initializer_zero(INITIALIZER_STRING);
+ initializer->string.string = *string;
+
+ return initializer;
+}
+
+static initializer_t *initializer_from_wide_string(array_type_t *const type,
+ wide_string_t *const string)