[GNU] Add __builtin_types_compatible_p

This commit is contained in:
Rui Ueyama 2020-09-06 21:06:56 +09:00
parent 7d80a5136d
commit 1433b404d6
4 changed files with 92 additions and 0 deletions

View File

@ -284,6 +284,7 @@ struct Type {
int size; // sizeof() value
int align; // alignment
bool is_unsigned; // unsigned or signed
Type *origin; // for type compatibility check
// Pointer-to or array-of type. We intentionally use the same member
// to represent pointer/array duality in C.
@ -348,6 +349,7 @@ extern Type *ty_double;
bool is_integer(Type *ty);
bool is_flonum(Type *ty);
bool is_numeric(Type *ty);
bool is_compatible(Type *t1, Type *t2);
Type *copy_type(Type *ty);
Type *pointer_to(Type *base);
Type *func_type(Type *return_ty);

10
parse.c
View File

@ -2611,6 +2611,7 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) {
// | "sizeof" unary
// | "_Alignof" "(" type-name ")"
// | "_Alignof" unary
// | "__builtin_types_compatible_p" "(" type-name, type-name, ")"
// | "__builtin_reg_class" "(" type-name ")"
// | ident
// | str
@ -2656,6 +2657,15 @@ static Node *primary(Token **rest, Token *tok) {
return new_ulong(node->ty->align, tok);
}
if (equal(tok, "__builtin_types_compatible_p")) {
tok = skip(tok->next, "(");
Type *t1 = typename(&tok, tok);
tok = skip(tok, ",");
Type *t2 = typename(&tok, tok);
*rest = skip(tok, ")");
return new_num(is_compatible(t1, t2), start);
}
if (equal(tok, "__builtin_reg_class")) {
tok = skip(tok->next, "(");
Type *ty = typename(&tok, tok);

33
test/builtin.c Normal file
View File

@ -0,0 +1,33 @@
#include "test.h"
int main() {
ASSERT(1, __builtin_types_compatible_p(int, int));
ASSERT(1, __builtin_types_compatible_p(double, double));
ASSERT(0, __builtin_types_compatible_p(int, long));
ASSERT(0, __builtin_types_compatible_p(long, float));
ASSERT(1, __builtin_types_compatible_p(int *, int *));
ASSERT(0, __builtin_types_compatible_p(short *, int *));
ASSERT(0, __builtin_types_compatible_p(int **, int *));
ASSERT(1, __builtin_types_compatible_p(const int, int));
ASSERT(0, __builtin_types_compatible_p(unsigned, int));
ASSERT(1, __builtin_types_compatible_p(signed, int));
ASSERT(0, __builtin_types_compatible_p(struct {int a;}, struct {int a;}));
ASSERT(1, __builtin_types_compatible_p(int (*)(void), int (*)(void)));
ASSERT(1, __builtin_types_compatible_p(void (*)(int), void (*)(int)));
ASSERT(1, __builtin_types_compatible_p(void (*)(int, double), void (*)(int, double)));
ASSERT(1, __builtin_types_compatible_p(int (*)(float, double), int (*)(float, double)));
ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int));
ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int (*)(float)));
ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int (*)(float, double, int)));
ASSERT(1, __builtin_types_compatible_p(double (*)(...), double (*)(...)));
ASSERT(0, __builtin_types_compatible_p(double (*)(...), double (*)(void)));
ASSERT(1, ({ typedef struct {int a;} T; __builtin_types_compatible_p(T, T); }));
ASSERT(1, ({ typedef struct {int a;} T; __builtin_types_compatible_p(T, const T); }));
ASSERT(1, ({ struct {int a; int b;} x; __builtin_types_compatible_p(typeof(x.a), typeof(x.b)); }));
printf("OK\n");
return 0;
}

47
type.c
View File

@ -38,9 +38,56 @@ bool is_numeric(Type *ty) {
return is_integer(ty) || is_flonum(ty);
}
bool is_compatible(Type *t1, Type *t2) {
if (t1 == t2)
return true;
if (t1->origin)
return is_compatible(t1->origin, t2);
if (t2->origin)
return is_compatible(t1, t2->origin);
if (t1->kind != t2->kind)
return false;
switch (t1->kind) {
case TY_CHAR:
case TY_SHORT:
case TY_INT:
case TY_LONG:
return t1->is_unsigned == t2->is_unsigned;
case TY_FLOAT:
case TY_DOUBLE:
return true;
case TY_PTR:
return is_compatible(t1->base, t2->base);
case TY_FUNC: {
if (!is_compatible(t1->return_ty, t2->return_ty))
return false;
if (t1->is_variadic != t2->is_variadic)
return false;
Type *p1 = t1->params;
Type *p2 = t2->params;
for (; p1 && p2; p1 = p1->next, p2 = p2->next)
if (!is_compatible(p1, p2))
return false;
return p1 == NULL && p2 == NULL;
}
case TY_ARRAY:
if (!is_compatible(t1->base, t2->base))
return false;
return t1->array_len < 0 && t2->array_len < 0 &&
t1->array_len == t2->array_len;
}
return false;
}
Type *copy_type(Type *ty) {
Type *ret = calloc(1, sizeof(Type));
*ret = *ty;
ret->origin = ty;
return ret;
}