+typedef enum assign_error_t {
+ ASSIGN_SUCCESS,
+ ASSIGN_ERROR_INCOMPATIBLE,
+ ASSIGN_ERROR_POINTER_QUALIFIER_MISSING,
+ ASSIGN_WARNING_POINTER_INCOMPATIBLE,
+ ASSIGN_WARNING_POINTER_FROM_INT,
+ ASSIGN_WARNING_INT_FROM_POINTER
+} assign_error_t;
+
+static void report_assign_error(assign_error_t error, type_t *orig_type_left,
+ const expression_t *const right,
+ const char *context,
+ const source_position_t *source_position)
+{
+ type_t *const orig_type_right = right->base.type;
+ type_t *const type_left = skip_typeref(orig_type_left);
+ type_t *const type_right = skip_typeref(orig_type_right);
+
+ switch (error) {
+ case ASSIGN_SUCCESS:
+ return;
+ case ASSIGN_ERROR_INCOMPATIBLE:
+ errorf(source_position,
+ "destination type '%T' in %s is incompatible with type '%T'",
+ orig_type_left, context, orig_type_right);
+ return;
+
+ case ASSIGN_ERROR_POINTER_QUALIFIER_MISSING: {
+ type_t *points_to_left
+ = skip_typeref(type_left->pointer.points_to);
+ type_t *points_to_right
+ = skip_typeref(type_right->pointer.points_to);
+
+ /* the left type has all qualifiers from the right type */
+ unsigned missing_qualifiers
+ = points_to_right->base.qualifiers & ~points_to_left->base.qualifiers;
+ errorf(source_position,
+ "destination type '%T' in %s from type '%T' lacks qualifiers '%Q' in pointed-to type",
+ orig_type_left, context, orig_type_right, missing_qualifiers);
+ return;
+ }
+
+ case ASSIGN_WARNING_POINTER_INCOMPATIBLE:
+ warningf(source_position,
+ "destination type '%T' in %s is incompatible with '%E' of type '%T'",
+ orig_type_left, context, right, orig_type_right);
+ return;
+
+ case ASSIGN_WARNING_POINTER_FROM_INT:
+ warningf(source_position,
+ "%s makes integer '%T' from pointer '%T' without a cast",
+ context, orig_type_left, orig_type_right);
+ return;
+
+ case ASSIGN_WARNING_INT_FROM_POINTER:
+ warningf(source_position,
+ "%s makes integer '%T' from pointer '%T' without a cast",
+ context, orig_type_left, orig_type_right);
+ return;
+
+ default:
+ panic("invalid error value");
+ }
+}
+