From 068aaef0ea684ecf5f891559aa8e1dae03b8428d Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 15 Dec 2025 11:16:38 +0100 Subject: c: support forward-declared enums This is a common non-standard GNU extension. --- c/c.c | 66 ++++++++++++++++++++++++++++++++---------------------------------- io.c | 2 +- type.c | 26 +++++++++++++++----------- type.h | 4 ++-- 4 files changed, 50 insertions(+), 48 deletions(-) diff --git a/c/c.c b/c/c.c index 7158bf3..594f38d 100644 --- a/c/c.c +++ b/c/c.c @@ -329,9 +329,10 @@ gettagged(struct comp *cm, struct span *span, enum typetag tt, const char *name, } } } + if (tt == TYENUM && ccopt.pedant) { + warn(span, "forward-declared enum is an extension"); + } Break2: - if (tt == TYENUM) - return mktype(0); td.t = tt; return envaddtagged(cm->env, mktagtype(name, &td), span)->ty; } @@ -383,8 +384,10 @@ assigncheck(union type t, const struct expr *src) warn(&src->span, "usage of '%ty' discards pointer qualifiers", src->ty); } return 1; - } - if (t.t == TYPTR && iszero(*src)) return 1; + } else if (t.t == TYPTR && srcty.t == TYPTR) { + warn(&src->span, "converting between incompatible pointer types '%ty' -> '%ty'", srcty, t); + return 1; + } else if (t.t == TYPTR && iszero(*src)) return 1; return 0; } @@ -448,9 +451,9 @@ static void /* 6.5.3.4 The sizeof and _Alignof operators */ sizeofalignofcheck(const struct span *span, enum toktag tt, union type ty, const struct expr *ex) { if (isincomplete(ty)) - error(span, "cannot apply %'tt to incomplete type '%ty'", tt); + error(span, "cannot apply %'tt to incomplete type '%ty'", tt, ty); else if (ty.t == TYFUNC) - error(span, "cannot apply %'tt to function type '%ty'", tt); + error(span, "cannot apply %'tt to function type '%ty'", tt, ty); else if (tt == TKWsizeof && ex && ex->t == EGETF && ex->fld.bitsiz) error(span, "cannot apply %'tt to bitfield", tt); if (tt != TKWsizeof && ex && ccopt.pedant) @@ -514,7 +517,7 @@ condtype(const struct expr *a, const struct expr *b) static void bintypeerr(const struct span *span, enum toktag tt, union type lhs, union type rhs) { - error(span, "bad operands to %tt: '%ty', '%ty'", tt, lhs, rhs); + error(span, "bad operands to %tt ('%ty', '%ty')", tt, lhs, rhs); } enum binopclass { /* binary operator type-checking classes */ @@ -1962,7 +1965,7 @@ inttyminmax(vlong *min, uvlong *max, enum typetag tt) * prefers to use unsigned types when possible). should add support for -fshort-enums */ static union type -buildenum(struct comp *cm, const char *name, const struct span *span) +buildenum(struct comp *cm, const char *name, const struct span *span, int id) { struct token tk; vlong tymin, minv = 0; @@ -2027,7 +2030,10 @@ buildenum(struct comp *cm, const char *name, const struct span *span) if (td.backing >= TYVLONG && !somelonglong && ccopt.cstd == STDC89 && ccopt.pedant) warn(span, "enum backing type is '%ty' in %M", mktype(td.backing)); - ty = mktagtype(name, &td); + if (id != -1) + ty = completetype(name, id, &td); + else + ty = mktagtype(name, &td); ty.backing = td.backing; return ty; } @@ -2051,29 +2057,23 @@ tagtype(struct comp *cm, enum toktag kind) return mktype(0); } t = gettagged(cm, &span, tt, tag, /* def? */ peek(cm, NULL) == ';'); - if (tt == TYENUM && !t.t) { - error(&tk.span, "cannot forward-declare enum"); - return mktype(TYINT); - } } else { - if (tt != TYENUM) { - if (tag) { - t = deftagged(cm, &span, tt, tag, mktype(0)); - if (t.t != tt || !isincomplete(t)) { - if (t.t != tt) - error(&tk.span, - "defining tagged type %'tk as %tt clashes with previous definition", - &tk, kind); - else - error(&tk.span, "redefinition of '%tt %s'", kind, tag, mktype(0)); - note(&span, "previous definition:"); - } + if (tag) { + t = deftagged(cm, &span, tt, tag, mktype(0)); + if (t.t != tt || !isincomplete(t)) { + if (t.t != tt) + error(&tk.span, + "defining tagged type %'tk as %tt clashes with previous definition", + &tk, kind); + else + error(&tk.span, "redefinition of '%tt %s'", kind, tag); + note(&span, "previous definition:"); } - t = buildagg(cm, tt, tag, tag ? typedata[t.dat].id : -1); - } else { - t = buildenum(cm, tag, &span); - if (tag) deftagged(cm, &span, TYENUM, tag, t); } + if (tt == TYENUM) + t = buildenum(cm, tag, &span, tag ? typedata[t.dat].id : -1); + else + t = buildagg(cm, tt, tag, tag ? typedata[t.dat].id : -1); } if (t.t != tt) { @@ -2485,11 +2485,9 @@ decltypes(struct comp *cm, struct decllist *list, const char **name, struct span vpush(&names, decl.name); vpush(&spans, decl.span); vpush(&qual, decl.qual); - if (isincomplete(decl.ty)) { - if (params.n > 1 || decl.ty.t != TYVOID - || decl.qual || decl.name || peek(cm, &tk) != ')') { - error(&decl.span, - "function parameter #%d has incomplete type (%tq)", + if (decl.ty.t == TYVOID) { + if (params.n > 1 || decl.qual || decl.name || peek(cm, &tk) != ')') { + error(&decl.span, "function parameter #%d has void type", params.n, decl.ty, qual.p[params.n-1]); } } diff --git a/io.c b/io.c index 529710b..1661e12 100644 --- a/io.c +++ b/io.c @@ -213,7 +213,7 @@ pritypebefore(struct wbuf *buf, union type ty, int qual) case TYPTR: chld = typechild(ty); n = pritypebefore(buf, chld, ty.flag & TFCHLDQUAL); - //if (chld.t != TYPTR) n += ioputc(buf, ' '); + n += bputc(buf, ' '); if (chld.t == TYARRAY || chld.t == TYFUNC) n += bputc(buf, '('); n += bputc(buf, '*'); diff --git a/type.c b/type.c index 53491c9..b18feb2 100644 --- a/type.c +++ b/type.c @@ -97,7 +97,7 @@ interntd(const struct typedata *td) } return i; } else if (tdequ(slot, td)) { - if (td->t == TYSTRUCT || td->t == TYUNION) + if (td->t == TYSTRUCT || td->t == TYUNION || td->t == TYENUM) goto Copy; return i; } @@ -114,6 +114,8 @@ isincomplete(union type t) case TYSTRUCT: case TYUNION: return typedata[t.dat].nmemb == 0; + case TYENUM: + return !typedata[t.dat].backing; } return 0; } @@ -124,7 +126,7 @@ typesize(union type t) if (isprim(t) || t.t == TYPTR) return targ_primsizes[t.t]; switch (t.t) { case TYENUM: - return targ_primsizes[t.backing]; + return targ_primsizes[typedata[t.dat].backing]; case TYARRAY: if (t.flag & TFCHLDPRIM) return targ_primsizes[t.child] * t.arrlen; @@ -142,7 +144,7 @@ typealign(union type t) if (isprim(t) || t.t == TYPTR) return targ_primalign[t.t]; switch (t.t) { case TYENUM: - return targ_primalign[t.backing]; + return targ_primalign[typedata[t.dat].backing]; case TYARRAY: return typealign(typechild(t)); case TYSTRUCT: @@ -190,7 +192,7 @@ completetype(const char *name, int id, struct typedata *td) assert(ttypenames[id] == name && "bad redefn"); else ttypenames[id] = name; - return mktype(td->t, .dat = interntd(td)); + return mktype(td->t, .dat = interntd(td), .backing = td->t == TYENUM ? td->backing : 0); } union type @@ -244,16 +246,18 @@ typedecay(union type t) bool /* 6.5.16.1 Simple assignment Constraints */ assigncompat(union type dst, union type src) { - union type ds, ss; if (dst.bits == src.bits) return 1; if (isarith(dst) && isarith(src)) return 1; if (dst.t == TYPTR && src.t == TYPTR) { - ds = typechild(dst); - ss = typechild(src); - if (ds.bits == ss.bits) return 1; - if (ss.t == TYVOID || ds.t == TYVOID) return 1; - if (ss.t == TYCHAR && in_range(ds.t, TYUCHAR, TYSCHAR)) return 1; - if (ds.t == TYCHAR && in_range(ss.t, TYUCHAR, TYSCHAR)) return 1; + union type ds = typechild(dst), ss = typechild(src); + if (ds.bits == ss.bits) return 1; /* T* with different qualifiers */ + if (ss.t == TYVOID || ds.t == TYVOID) return 1; /* T* <-> void* */ + enum typetag dt = scalartypet(ds), /* handle enums */ + st = scalartypet(ss); + if (!dt || !st) return 0; /* unequal incomplete enums */ + /* plain char exception */ + if (st == TYCHAR && in_range(dt, TYUCHAR, TYSCHAR)) return 1; + if (dt == TYCHAR && in_range(st, TYUCHAR, TYSCHAR)) return 1; } else if (dst.t == TYBOOL && src.t == TYPTR) return 1; return 0; diff --git a/type.h b/type.h index 4124dff..decef3c 100644 --- a/type.h +++ b/type.h @@ -136,7 +136,7 @@ union type cvtarith(union type a, union type b); static inline union type typechild(union type t) { - if (t.t == TYENUM) return mktype(t.backing); + if (t.t == TYENUM) return mktype(t.backing ? t.backing : typedata[t.dat].backing); if (t.flag & TFCHLDPRIM) return mktype(t.child); if (t.flag & TFCHLDISDAT) { union type chld = mktype(typedata[t.dat].t, .dat = t.dat); @@ -148,7 +148,7 @@ typechild(union type t) static inline enum typetag scalartypet(union type t) { - if (t.t == TYENUM) return t.backing; + if (t.t == TYENUM) return t.backing ? t.backing : typedata[t.dat].backing; if (isptrcvt(t)) return TYPTR; return t.t; } -- cgit v1.2.3