tyname.c revision 1.21
1/*	$NetBSD: tyname.c,v 1.21 2021/01/03 15:55:18 rillig Exp $	*/
2
3/*-
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37#if defined(__RCSID) && !defined(lint)
38__RCSID("$NetBSD: tyname.c,v 1.21 2021/01/03 15:55:18 rillig Exp $");
39#endif
40
41#include <limits.h>
42#include <string.h>
43#include <stdlib.h>
44#include <err.h>
45
46#include PASS
47
48#ifndef LERROR
49#define LERROR(fmt, args...) \
50	do { \
51		(void)warnx("%s, %d: " fmt, __FILE__, __LINE__, ##args); \
52		abort(); \
53	} while (/*CONSTCOND*/0)
54#endif
55
56/* A tree of strings. */
57typedef struct name_tree_node {
58	char *ntn_name;
59	struct name_tree_node *ntn_less;
60	struct name_tree_node *ntn_greater;
61} name_tree_node;
62
63/* A growable string buffer. */
64typedef struct buffer {
65	size_t	len;
66	size_t	cap;
67	char *	data;
68} buffer;
69
70static name_tree_node *type_names;
71
72static name_tree_node *
73new_name_tree_node(const char *name)
74{
75	name_tree_node *n;
76
77	n = xmalloc(sizeof(*n));
78	n->ntn_name = xstrdup(name);
79	n->ntn_less = NULL;
80	n->ntn_greater = NULL;
81	return n;
82}
83
84/* Return the canonical instance of the string, with unlimited life time. */
85static const char *
86intern(const char *name)
87{
88	name_tree_node *n = type_names;
89	int cmp;
90
91	if (n == NULL) {
92		n = new_name_tree_node(name);
93		type_names = n;
94		return n->ntn_name;
95	}
96
97	while ((cmp = strcmp(name, n->ntn_name)) != 0) {
98		if (cmp < 0) {
99			if (n->ntn_less == NULL) {
100				n->ntn_less = new_name_tree_node(name);
101				return n->ntn_less->ntn_name;
102			}
103			n = n->ntn_less;
104		} else {
105			if (n->ntn_greater == NULL) {
106				n->ntn_greater = new_name_tree_node(name);
107				return n->ntn_greater->ntn_name;
108			}
109			n = n->ntn_greater;
110		}
111	}
112	return n->ntn_name;
113}
114
115static void
116buf_init(buffer *buf)
117{
118	buf->len = 0;
119	buf->cap = 128;
120	buf->data = xmalloc(buf->cap);
121	buf->data[0] = '\0';
122}
123
124static void
125buf_done(buffer *buf)
126{
127	free(buf->data);
128}
129
130static void
131buf_add(buffer *buf, const char *s)
132{
133	size_t len = strlen(s);
134
135	while (buf->len + len + 1 >= buf->cap) {
136		buf->data = xrealloc(buf->data, 2 * buf->cap);
137		buf->cap = 2 * buf->cap;
138	}
139
140	memcpy(buf->data + buf->len, s, len + 1);
141	buf->len += len;
142}
143
144static void
145buf_add_int(buffer *buf, int n)
146{
147	char num[1 + sizeof(n) * CHAR_BIT + 1];
148
149	snprintf(num, sizeof num, "%d", n);
150	buf_add(buf, num);
151}
152
153const char *
154tspec_name(tspec_t t)
155{
156	switch (t) {
157	case SIGNED:	return "signed";
158	case UNSIGN:	return "unsigned";
159	case BOOL:	return "_Bool";
160	case CHAR:	return "char";
161	case SCHAR:	return "signed char";
162	case UCHAR:	return "unsigned char";
163	case SHORT:	return "short";
164	case USHORT:	return "unsigned short";
165	case INT:	return "int";
166	case UINT:	return "unsigned int";
167	case LONG:	return "long";
168	case ULONG:	return "unsigned long";
169	case QUAD:	return "long long";
170	case UQUAD:	return "unsigned long long";
171#ifdef INT128_SIZE
172	case INT128:	return "__int128_t";
173	case UINT128:	return "__uint128_t";
174#endif
175	case FLOAT:	return "float";
176	case DOUBLE:	return "double";
177	case LDOUBLE:	return "long double";
178	case VOID:	return "void";
179	case STRUCT:	return "struct";
180	case UNION:	return "union";
181	case ENUM:	return "enum";
182	case PTR:	return "pointer";
183	case ARRAY:	return "array";
184	case FUNC:	return "function";
185	case COMPLEX:	return "_Complex";
186	case FCOMPLEX:	return "float _Complex";
187	case DCOMPLEX:	return "double _Complex";
188	case LCOMPLEX:	return "long double _Complex";
189	default:
190		LERROR("tspec_name(%d)", t);
191		return NULL;
192	}
193}
194
195int
196sametype(const type_t *t1, const type_t *t2)
197{
198	tspec_t	t;
199
200	if (t1->t_tspec != t2->t_tspec)
201		return 0;
202
203	/* Ignore const/void */
204
205	switch (t = t1->t_tspec) {
206	case BOOL:
207	case CHAR:
208	case UCHAR:
209	case SCHAR:
210	case SHORT:
211	case USHORT:
212	case INT:
213	case UINT:
214	case LONG:
215	case ULONG:
216	case QUAD:
217	case UQUAD:
218#ifdef INT128_SIZE
219	case INT128:
220	case UINT128:
221#endif
222	case FLOAT:
223	case DOUBLE:
224	case LDOUBLE:
225	case VOID:
226	case FUNC:
227	case COMPLEX:
228	case FCOMPLEX:
229	case DCOMPLEX:
230	case LCOMPLEX:
231		return 1;
232	case ARRAY:
233		if (t1->t_dim != t2->t_dim)
234			return 0;
235		/*FALLTHROUGH*/
236	case PTR:
237		return sametype(t1->t_subt, t2->t_subt);
238	case ENUM:
239#ifdef t_enum
240		return strcmp(t1->t_enum->etag->s_name,
241		    t2->t_enum->etag->s_name) == 0;
242#else
243		return 1;
244#endif
245	case STRUCT:
246	case UNION:
247#ifdef t_str
248		return strcmp(t1->t_str->stag->s_name,
249		    t2->t_str->stag->s_name) == 0;
250#else
251		return 1;
252#endif
253	default:
254		LERROR("tyname(%d)", t);
255		return 0;
256	}
257}
258
259static void
260type_name_of_function(buffer *buf, const type_t *tp)
261{
262	const char *sep = "";
263
264	buf_add(buf, "(");
265	if (tp->t_proto) {
266#ifdef t_enum /* lint1 */
267		sym_t *arg;
268
269		for (arg = tp->t_args; arg != NULL; arg = arg->s_next) {
270			buf_add(buf, sep), sep = ", ";
271			buf_add(buf, type_name(arg->s_type));
272		}
273#else /* lint2 */
274		type_t **argtype;
275
276		for (argtype = tp->t_args; *argtype != NULL; argtype++) {
277			buf_add(buf, sep), sep = ", ";
278			buf_add(buf, type_name(*argtype));
279		}
280#endif
281	}
282	if (tp->t_vararg) {
283		buf_add(buf, sep), sep = ", ";
284		buf_add(buf, "...");
285	}
286	buf_add(buf, ") returning ");
287	buf_add(buf, type_name(tp->t_subt));
288}
289
290const char *
291type_name(const type_t *tp)
292{
293	tspec_t t;
294	buffer buf;
295	const char *name;
296
297	if (tp == NULL)
298		return "(null)";
299
300	/*
301	 * XXX: Why is this necessary, and in which cases does this apply?
302	 * Shouldn't the type be an ENUM from the beginning?
303	 */
304	if ((t = tp->t_tspec) == INT && tp->t_isenum)
305		t = ENUM;
306
307	buf_init(&buf);
308	if (tp->t_const)
309		buf_add(&buf, "const ");
310	if (tp->t_volatile)
311		buf_add(&buf, "volatile ");
312	buf_add(&buf, tspec_name(t));
313
314	switch (t) {
315	case BOOL:
316	case CHAR:
317	case UCHAR:
318	case SCHAR:
319	case SHORT:
320	case USHORT:
321	case INT:
322	case UINT:
323	case LONG:
324	case ULONG:
325	case QUAD:
326	case UQUAD:
327#ifdef INT128_SIZE
328	case INT128:
329	case UINT128:
330#endif
331	case FLOAT:
332	case DOUBLE:
333	case LDOUBLE:
334	case VOID:
335	case COMPLEX:
336	case FCOMPLEX:
337	case DCOMPLEX:
338	case LCOMPLEX:
339	case SIGNED:
340	case UNSIGN:
341		break;
342	case PTR:
343		buf_add(&buf, " to ");
344		buf_add(&buf, type_name(tp->t_subt));
345		break;
346	case ENUM:
347		buf_add(&buf, " ");
348#ifdef t_enum
349		buf_add(&buf, tp->t_enum->etag->s_name);
350#else
351		buf_add(&buf,
352		    tp->t_isuniqpos ? "*anonymous*" : tp->t_tag->h_name);
353#endif
354		break;
355	case STRUCT:
356	case UNION:
357		buf_add(&buf, " ");
358#ifdef t_str
359		buf_add(&buf, tp->t_str->stag->s_name);
360#else
361		buf_add(&buf,
362		    tp->t_isuniqpos ? "*anonymous*" : tp->t_tag->h_name);
363#endif
364		break;
365	case ARRAY:
366		buf_add(&buf, " of ");
367		buf_add(&buf, type_name(tp->t_subt));
368		buf_add(&buf, "[");
369		buf_add_int(&buf, tp->t_dim);
370		buf_add(&buf, "]");
371		break;
372	case FUNC:
373		type_name_of_function(&buf, tp);
374		break;
375
376	default:
377		LERROR("type_name(%d)", t);
378	}
379
380	name = intern(buf.data);
381	buf_done(&buf);
382	return name;
383}
384