+/**
+ * Parse a delete expression
+ * ISO/IEC 14882:1998(E) §5.3.5
+ */
+static expression_t *parse_delete(void)
+{
+ expression_t *const result = allocate_expression_zero(EXPR_UNARY_DELETE);
+ result->base.type = type_void;
+
+ eat(T_delete);
+
+ if (token.type == '[') {
+ next_token();
+ result->kind = EXPR_UNARY_DELETE_ARRAY;
+ expect(']');
+end_error:;
+ }
+
+ expression_t *const value = parse_sub_expression(PREC_CAST);
+ result->unary.value = value;
+
+ type_t *const type = skip_typeref(value->base.type);
+ if (!is_type_pointer(type)) {
+ errorf(&value->base.source_position,
+ "operand of delete must have pointer type");
+ } else if (warning.other &&
+ is_type_atomic(skip_typeref(type->pointer.points_to), ATOMIC_TYPE_VOID)) {
+ warningf(&value->base.source_position,
+ "deleting 'void*' is undefined");
+ }
+
+ return result;
+}
+
+/**
+ * Parse a throw expression
+ * ISO/IEC 14882:1998(E) §15:1
+ */
+static expression_t *parse_throw(void)
+{
+ expression_t *const result = allocate_expression_zero(EXPR_UNARY_THROW);
+ result->base.type = type_void;
+
+ eat(T_throw);
+
+ expression_t *value = NULL;
+ switch (token.type) {
+ EXPRESSION_START {
+ value = parse_assignment_expression();
+ /* ISO/IEC 14882:1998(E) §15.1:3 */
+ type_t *const orig_type = value->base.type;
+ type_t *const type = skip_typeref(orig_type);
+ if (is_type_incomplete(type)) {
+ errorf(&value->base.source_position,
+ "cannot throw object of incomplete type '%T'", orig_type);
+ } else if (is_type_pointer(type)) {
+ type_t *const points_to = skip_typeref(type->pointer.points_to);
+ if (is_type_incomplete(points_to) &&
+ !is_type_atomic(points_to, ATOMIC_TYPE_VOID)) {
+ errorf(&value->base.source_position,
+ "cannot throw pointer to incomplete type '%T'", orig_type);
+ }
+ }
+ }
+
+ default:
+ break;
+ }
+ result->unary.value = value;
+
+ return result;
+}
+