Skip to main content

rucc_types/
compat.rs

1//! Type compatibility and the composite type.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.2.
4//!
5//! Compatibility, 6.2.7, is the looser relation that identity is not. Two types are the same
6//! when they are the same id, which is what the interner is for; two types are compatible when
7//! C says a declaration of one may follow a declaration of the other. `int f(int a[3])` and
8//! `int f(int *a)` declare the same function, an `enum` is compatible with whatever it is
9//! represented in, and an array with a size is compatible with one without.
10//!
11//! The composite type, 6.2.7p3, is what a redeclaration leaves behind: the type that takes the
12//! array size from whichever declaration had one and the parameter list from whichever
13//! declaration was a prototype. `extern int a[]; int a[4];` has to end up with an array of
14//! four, and a compiler that keeps the first type instead has lost the size for good.
15//!
16//! The rules here were checked by writing each pair of declarations and seeing which ones gcc
17//! 13.3 and clang 18 refuse. They agree everywhere except one place, recorded on
18//! [`records`]: clang implements the C23 rule that an identical structure redefinition in one
19//! translation unit is the same type, and gcc 13.3 still rejects it. gcc 16 takes it too.
20//!
21//! Nothing in here needs the target. Everything target dependent about a type has already been
22//! decided by the time it is in the table: an enumeration knows what it is represented in and
23//! an array knows how many elements it has.
24
25use crate::kind::{ArrayLen, FloatKind, FunctionType, IntKind, RecordId, TypeKind};
26use crate::types::{TypeId, Types};
27
28/// Whether a declaration of `left` and a declaration of `right` declare the same thing, 6.2.7.
29///
30/// Identity implies compatibility and is one integer comparison, so this only does any work
31/// when the two ids differ.
32#[must_use]
33pub fn compatible(types: &Types, left: TypeId, right: TypeId) -> bool {
34    let mut assumed = Vec::new();
35    same(types, left, right, &mut assumed)
36}
37
38/// The composite type of two compatible types, 6.2.7p3, and [`None`] when they are not
39/// compatible.
40///
41/// It takes whatever each side knows: the size from the declaration that had one, the parameter
42/// list from the declaration that was a prototype. This is what a caller merging two
43/// declarations of one name should store, rather than either type it was given.
44pub fn composite(types: &mut Types, left: TypeId, right: TypeId) -> Option<TypeId> {
45    if !compatible(types, left, right) {
46        return None;
47    }
48    Some(build(types, left, right))
49}
50
51/// The type a parameter declared as `id` really has, 6.7.6.3p7 and p8.
52///
53/// An array parameter is a pointer to its element and a function parameter is a pointer to the
54/// function, which is why `int f(int a[3])` and `int f(int *a)` declare the same function. The
55/// qualifiers on the outermost node go too, so `void f(const int)` and `void f(int)` do as
56/// well: a `const` there is a promise the function makes to itself and not part of its type.
57///
58/// [`FunctionType::params`] is defined to hold types this has already been applied to, so this
59/// belongs to whoever builds the type out of a declarator rather than to the comparison below.
60pub fn adjust_parameter(types: &mut Types, id: TypeId) -> TypeId {
61    let canonical = types.canonical(id);
62    match types.kind(canonical) {
63        // The element keeps its own qualifiers. The ones C99 allows inside the brackets belong
64        // to the pointer that replaces the array, and the parser is what puts them there.
65        TypeKind::Array { elem, .. } => types.pointer(elem),
66        TypeKind::Function(_) => types.pointer(canonical),
67        _ => types.unqualified(id),
68    }
69}
70
71/// Compatibility, with a stack of record pairs already assumed compatible.
72///
73/// The stack is what makes a self referential structure terminate. `struct node { struct node
74/// *next; }` compared against another declaration of itself comes back to the same pair through
75/// the pointer, and the second time it is an assumption rather than a question.
76fn same(
77    types: &Types,
78    left: TypeId,
79    right: TypeId,
80    assumed: &mut Vec<(RecordId, RecordId)>,
81) -> bool {
82    let left = types.canonical(left);
83    let right = types.canonical(right);
84    if left == right {
85        return true;
86    }
87    if types.quals(left) != types.quals(right) {
88        // The qualifiers have to match exactly, which is what keeps `const int *` and `int *`
89        // apart as parameter types.
90        return false;
91    }
92    shapes(types, left, right, assumed)
93}
94
95/// The same question with the outermost qualifiers already agreed about, on two canonical ids.
96///
97/// Split out of [`same`] for the one caller that has to ask it with the qualifiers set aside,
98/// which is [`through_transparent`]: a member of glibc's socket union is a `struct sockaddr
99/// *__restrict` and the parameter it faces is a `struct sockaddr *`, and a `restrict` there is a
100/// promise the function makes to itself rather than part of the type it takes. Stripping the
101/// qualifier instead would mean interning a type, which this module has no mutable table for.
102fn shapes(
103    types: &Types,
104    left: TypeId,
105    right: TypeId,
106    assumed: &mut Vec<(RecordId, RecordId)>,
107) -> bool {
108    if left == right {
109        return true;
110    }
111    match (types.kind(left), types.kind(right)) {
112        // Two different enumeration declarations are two different types. Each is compatible
113        // with what it is represented in, and whether a redefinition of one tag makes the same
114        // type again is a question about enumerator values, which live with the declaration
115        // rather than in this table.
116        (TypeKind::Enum(_), TypeKind::Enum(_)) => false,
117        // An enumeration is compatible with the type it is represented in. Both compilers agree,
118        // and it is visible in that a `_Generic` cannot list `enum E` and `unsigned int` both.
119        (TypeKind::Enum(id), _) => match types.enum_info(id).underlying {
120            Some(underlying) => same(types, underlying, right, assumed),
121            None => false,
122        },
123        (_, TypeKind::Enum(id)) => match types.enum_info(id).underlying {
124            Some(underlying) => same(types, left, underlying, assumed),
125            None => false,
126        },
127        (TypeKind::Pointer(a), TypeKind::Pointer(b))
128        | (TypeKind::Atomic(a), TypeKind::Atomic(b)) => same(types, a, b, assumed),
129        (TypeKind::Array { elem: a, len: x }, TypeKind::Array { elem: b, len: y }) => {
130            lengths_agree(x, y) && same(types, a, b, assumed)
131        }
132        (TypeKind::Vector { elem: a, len: x }, TypeKind::Vector { elem: b, len: y }) => {
133            x == y && same(types, a, b, assumed)
134        }
135        (TypeKind::Function(a), TypeKind::Function(b)) => {
136            functions(types, types.signature(a), types.signature(b), assumed)
137        }
138        (TypeKind::Record(a), TypeKind::Record(b)) => records(types, a, b, assumed),
139        _ => false,
140    }
141}
142
143/// Whether two array lengths are compatible.
144///
145/// Only two constant sizes can disagree. An array whose size nobody wrote is compatible with
146/// any of them, and so is a variable length one, whose size is not known until it runs.
147fn lengths_agree(left: ArrayLen, right: ArrayLen) -> bool {
148    match (left, right) {
149        (ArrayLen::Fixed(a), ArrayLen::Fixed(b)) => a == b,
150        _ => true,
151    }
152}
153
154/// Whether two function types are compatible, 6.7.6.3p15.
155fn functions(
156    types: &Types,
157    left: &FunctionType,
158    right: &FunctionType,
159    assumed: &mut Vec<(RecordId, RecordId)>,
160) -> bool {
161    // Two conventions are two types, as they are to gcc, which calls a declaration of the one
162    // after a declaration of the other conflicting and a pointer to the one assigned to a pointer
163    // to the other incompatible. Nothing about a call could go right across the difference: the
164    // arguments would be in the other registers and the callee would clobber what the caller
165    // expects it to keep.
166    if left.convention != right.convention || !same(types, left.ret, right.ret, assumed) {
167        return false;
168    }
169    match (left.prototyped, right.prototyped) {
170        (true, true) => {
171            left.variadic == right.variadic
172                && left.params.len() == right.params.len()
173                && left.params.iter().zip(&right.params).all(|(&a, &b)| {
174                    same(types, a, b, assumed) || through_transparent(types, a, b, assumed)
175                })
176        }
177        // An old style definition is the one unprototyped type that knows what its parameters
178        // are, and 6.7.6.3p15 holds it to a stricter rule than a declaration that knows nothing:
179        // the counts have to agree and each prototype parameter has to be compatible with the
180        // promoted type of the identifier facing it, which is what the list holds.
181        (true, false) if !right.params.is_empty() => defines(types, left, right, assumed),
182        (false, true) if !left.params.is_empty() => defines(types, right, left, assumed),
183        // An old style declaration says nothing about the parameters, so it is compatible with a
184        // prototype only when the call would have gone the same way regardless: no `...`, and no
185        // parameter the default argument promotions would have changed on the way in.
186        (true, false) => stands_for(types, left),
187        (false, true) => stands_for(types, right),
188        (false, false) => true,
189    }
190}
191
192/// Whether one of two parameter types is a transparent union the other is a member of.
193///
194/// This is the half of `transparent_union` that is about declarations rather than about values.
195/// `accept` takes a union of eleven socket address pointers, and a program that declares it as
196/// taking a `struct sockaddr *` has declared the same function, which is what lets gnulib assign
197/// the one to a pointer to the other and what the attribute is for. Either side may be the union,
198/// since a program may declare the function either way round and the pair has to be compatible
199/// both ways for a redeclaration to be accepted.
200///
201/// Any member counts and not only the first. The first member is what decides how the union is
202/// passed, so it is the one the attribute needs to be well defined, but every member is a type the
203/// union takes a value of and gcc accepts a declaration written with any of them.
204///
205/// A bit-field member is not one of them, since there is no value of a bit-field's type to pass
206/// and nothing could be assigned into it whole.
207fn through_transparent(
208    types: &Types,
209    left: TypeId,
210    right: TypeId,
211    assumed: &mut Vec<(RecordId, RecordId)>,
212) -> bool {
213    member_of(types, left, right, assumed) || member_of(types, right, left, assumed)
214}
215
216/// Whether the first type is a transparent union and the second is one of its members.
217fn member_of(
218    types: &Types,
219    union: TypeId,
220    other: TypeId,
221    assumed: &mut Vec<(RecordId, RecordId)>,
222) -> bool {
223    let TypeKind::Record(id) = types.kind(types.canonical(union)) else { return false };
224    let info = types.record_info(id);
225    if !info.transparent {
226        return false;
227    }
228    let other = types.canonical(other);
229    info.fields.iter().any(|field| {
230        field.bits.is_none() && shapes(types, types.canonical(field.ty), other, assumed)
231    })
232}
233
234/// Whether a prototype and an old style definition describe the same function, 6.7.6.3p15.
235///
236/// The rule as written is that the counts agree and each prototype parameter is compatible with
237/// the promoted type of the identifier facing it. Taken literally that makes `int f(char);` and a
238/// definition of `f` with a `char` identifier two different functions, since `char` promotes to
239/// `int`, and every compiler takes that pair because all the code written this way is written
240/// against a header. So a prototype parameter the promotions would have changed is allowed to
241/// face what it changes into, which is the one relaxation and is what makes the pair work.
242fn defines(
243    types: &Types,
244    proto: &FunctionType,
245    def: &FunctionType,
246    assumed: &mut Vec<(RecordId, RecordId)>,
247) -> bool {
248    !proto.variadic
249        && proto.params.len() == def.params.len()
250        && proto
251            .params
252            .iter()
253            .zip(&def.params)
254            .all(|(&a, &b)| same(types, a, b, assumed) || promotes_to(types, a, b))
255}
256
257/// Whether the default argument promotions turn the first type into the second.
258fn promotes_to(types: &Types, from: TypeId, to: TypeId) -> bool {
259    if survives_promotion(types, from) {
260        return false;
261    }
262    let to = types.kind(types.canonical(to));
263    match types.kind(types.canonical(from)) {
264        TypeKind::Float(FloatKind::Float) => to == TypeKind::Float(FloatKind::Double),
265        // Everything else the promotions touch is narrower than an `int` and becomes one. The
266        // target where that is not so is one where `int` is no wider than a `short`, which none
267        // of the targets here is.
268        _ => to == TypeKind::Int(IntKind::Int),
269    }
270}
271
272/// Whether an old style declaration of a function could stand for this prototype.
273fn stands_for(types: &Types, signature: &FunctionType) -> bool {
274    !signature.variadic && signature.params.iter().all(|&param| survives_promotion(types, param))
275}
276
277/// Whether a parameter type is one the default argument promotions leave alone.
278///
279/// The `float` case is the one that matters: an old style call passes a `double`, so a prototype
280/// taking a `float` is a different function from the same name declared without one, and both
281/// compilers refuse the pair.
282fn survives_promotion(types: &Types, id: TypeId) -> bool {
283    match types.kind(types.canonical(id)) {
284        TypeKind::Bool => false,
285        TypeKind::Int(kind) => kind.rank() >= IntKind::Int.rank(),
286        TypeKind::Float(FloatKind::Float) => false,
287        // An enumeration is compatible with what it is represented in, so it comes through
288        // whenever that type does.
289        TypeKind::Enum(id) => match types.enum_info(id).underlying {
290            Some(underlying) => survives_promotion(types, underlying),
291            None => false,
292        },
293        // Everything else, `_BitInt` included, is its own promotion.
294        _ => true,
295    }
296}
297
298/// Whether two record declarations are the same type.
299///
300/// The same declaration always is. Two different ones are in C23 when they have the same tag and
301/// the same members, which is the rule that lets a header be included twice without a guard.
302/// clang 18 and gcc 16 implement it, and gcc 13.3 still rejects the redefinition outright. The
303/// checker leans on it when a tag is defined again in C23. In the older
304/// dialects the question does not arise, because a second definition of a tag in one scope is
305/// refused before anything asks whether the two types match.
306fn records(
307    types: &Types,
308    left: RecordId,
309    right: RecordId,
310    assumed: &mut Vec<(RecordId, RecordId)>,
311) -> bool {
312    if left == right || assumed.contains(&(left, right)) {
313        return true;
314    }
315    let a = types.record_info(left);
316    let b = types.record_info(right);
317    if a.kind != b.kind || a.tag.is_none() || a.tag != b.tag {
318        // An anonymous record is compatible with nothing but itself. There is no name by which a
319        // second declaration could be claiming to be the same type.
320        return false;
321    }
322    if a.layout.is_none() || b.layout.is_none() || a.fields.len() != b.fields.len() {
323        // An incomplete declaration has no members to compare. Two mentions of one tag in one
324        // scope are one declaration and one id, so they never reach here.
325        return false;
326    }
327    assumed.push((left, right));
328    let answer = a
329        .fields
330        .iter()
331        .zip(&b.fields)
332        .all(|(x, y)| x.name == y.name && x.bits == y.bits && same(types, x.ty, y.ty, assumed));
333    assumed.pop();
334    answer
335}
336
337/// The composite of two types already known to be compatible.
338fn build(types: &mut Types, left: TypeId, right: TypeId) -> TypeId {
339    if left == right {
340        return left;
341    }
342    let canonical = types.canonical(left);
343    match (types.kind(canonical), types.kind(types.canonical(right))) {
344        (TypeKind::Array { elem: a, len: x }, TypeKind::Array { elem: b, len: y }) => {
345            let elem = build(types, a, b);
346            // The declaration that knew the size is the one to take it from, whichever side it
347            // was. An array type carries no qualifiers of its own, they are on the element.
348            let len = if matches!(x, ArrayLen::Fixed(_)) { x } else { y };
349            types.array(elem, len)
350        }
351        (TypeKind::Pointer(a), TypeKind::Pointer(b)) => {
352            let inner = build(types, a, b);
353            let quals = types.quals(canonical);
354            let pointer = types.pointer(inner);
355            types.qualified(pointer, quals)
356        }
357        (TypeKind::Function(a), TypeKind::Function(b)) => {
358            let a = types.signature(a).clone();
359            let b = types.signature(b).clone();
360            composite_function(types, &a, &b)
361        }
362        // Everything else has nothing to combine, and the type as it was written is the better
363        // of the two answers because a diagnostic can print the name the program used.
364        _ => left,
365    }
366}
367
368/// The composite of two compatible function types.
369fn composite_function(types: &mut Types, left: &FunctionType, right: &FunctionType) -> TypeId {
370    let ret = build(types, left.ret, right.ret);
371    // The prototype wins, because it is the declaration that knows something. This is what makes
372    // `void f(); void f(int);` a function of one `int` afterwards, so that the calls written
373    // between the two declarations can still be checked against something.
374    let (params, variadic, prototyped) = match (left.prototyped, right.prototyped) {
375        (true, true) => {
376            let params =
377                left.params.iter().zip(&right.params).map(|(&a, &b)| build(types, a, b)).collect();
378            (params, left.variadic, true)
379        }
380        (true, false) => (left.params.clone(), left.variadic, true),
381        (false, true) => (right.params.clone(), right.variadic, true),
382        // Neither is a prototype, so neither makes a call checkable. What is still worth keeping
383        // is an old style definition's parameter list, which is the only thing an unprototyped
384        // type ever has one of and which is what its own lowering reads.
385        (false, false) => {
386            let params =
387                if left.params.is_empty() { right.params.clone() } else { left.params.clone() };
388            (params, false, false)
389        }
390    };
391    // The two conventions are the same one, since the types would not be compatible otherwise.
392    let convention = left.convention;
393    types.function(FunctionType { ret, params, variadic, prototyped, convention })
394}