// Inferno utils/cc/dpchk.c // http://code.google.com/p/inferno-os/source/browse/utils/cc/dpchk.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) // Portions Copyright © 2004,2006 Bruce Ellis // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others // Portions Copyright © 2009 The Go Authors. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include #include "cc.h" #include "y.tab.h" enum { Fnone = 0, Fl, Fvl, Fignor, Fstar, Fadj, Fverb = 10, }; typedef struct Tprot Tprot; struct Tprot { Type* type; Bits flag; Tprot* link; }; typedef struct Tname Tname; struct Tname { char* name; int param; int count; Tname* link; Tprot* prot; }; static Type* indchar; static uchar flagbits[512]; static char* lastfmt; static int lastadj; static int lastverb; static int nstar; static Tprot* tprot; static Tname* tname; void argflag(int c, int v) { switch(v) { case Fignor: case Fstar: case Fl: case Fvl: flagbits[c] = v; break; case Fverb: flagbits[c] = lastverb; /*print("flag-v %c %d\n", c, lastadj);*/ lastverb++; break; case Fadj: flagbits[c] = lastadj; /*print("flag-l %c %d\n", c, lastadj);*/ lastadj++; break; } } Bits getflag(char *s) { Bits flag; int f; Fmt fmt; Rune c; flag = zbits; nstar = 0; fmtstrinit(&fmt); for(;;) { s += chartorune(&c, s); if(c == 0 || c >= nelem(flagbits)) break; fmtrune(&fmt, c); f = flagbits[c]; switch(f) { case Fnone: argflag(c, Fverb); f = flagbits[c]; break; case Fstar: nstar++; case Fignor: continue; case Fl: if(bset(flag, Fl)) flag = bor(flag, blsh(Fvl)); } flag = bor(flag, blsh(f)); if(f >= Fverb) break; } free(lastfmt); lastfmt = fmtstrflush(&fmt); return flag; } static void newprot(Sym *m, Type *t, char *s, Tprot **prot) { Bits flag; Tprot *l; if(t == T) { warn(Z, "%s: newprot: type not defined", m->name); return; } flag = getflag(s); for(l=*prot; l; l=l->link) if(beq(flag, l->flag) && sametype(t, l->type)) return; l = alloc(sizeof(*l)); l->type = t; l->flag = flag; l->link = *prot; *prot = l; } static Tname* newname(char *s, int p, int count) { Tname *l; for(l=tname; l; l=l->link) if(strcmp(l->name, s) == 0) { if(p >= 0 && l->param != p) yyerror("vargck %s already defined\n", s); return l; } if(p < 0) return nil; l = alloc(sizeof(*l)); l->name = s; l->param = p; l->link = tname; l->count = count; tname = l; return l; } void arginit(void) { int i; /* debug['F'] = 1;*/ /* debug['w'] = 1;*/ lastadj = Fadj; lastverb = Fverb; indchar = typ(TIND, types[TCHAR]); memset(flagbits, Fnone, sizeof(flagbits)); for(i='0'; i<='9'; i++) argflag(i, Fignor); argflag('.', Fignor); argflag('#', Fignor); argflag('u', Fignor); argflag('h', Fignor); argflag('+', Fignor); argflag('-', Fignor); argflag('*', Fstar); argflag('l', Fl); argflag('o', Fverb); flagbits['x'] = flagbits['o']; flagbits['X'] = flagbits['o']; } static char* getquoted(void) { int c; Rune r; Fmt fmt; c = getnsc(); if(c != '"') return nil; fmtstrinit(&fmt); for(;;) { r = getr(); if(r == '\n') { free(fmtstrflush(&fmt)); return nil; } if(r == '"') break; fmtrune(&fmt, r); } free(lastfmt); lastfmt = fmtstrflush(&fmt); return strdup(lastfmt); } void pragvararg(void) { Sym *s; int n, c; char *t; Type *ty; Tname *l; if(!debug['F']) goto out; s = getsym(); if(s && strcmp(s->name, "argpos") == 0) goto ckpos; if(s && strcmp(s->name, "type") == 0) goto cktype; if(s && strcmp(s->name, "flag") == 0) goto ckflag; if(s && strcmp(s->name, "countpos") == 0) goto ckcount; yyerror("syntax in #pragma varargck"); goto out; ckpos: /*#pragma varargck argpos warn 2*/ s = getsym(); if(s == S) goto bad; n = getnsn(); if(n < 0) goto bad; newname(s->name, n, 0); goto out; ckcount: /*#pragma varargck countpos name 2*/ s = getsym(); if(s == S) goto bad; n = getnsn(); if(n < 0) goto bad; newname(s->name, 0, n); goto out; ckflag: /*#pragma varargck flag 'c'*/ c = getnsc(); if(c != '\'') goto bad; c = getr(); if(c == '\\') c = getr(); else if(c == '\'') goto bad; if(c == '\n') goto bad; if(getc() != '\'') goto bad; argflag(c, Fignor); goto out; cktype: c = getnsc(); unget(c); if(c != '"') { /*#pragma varargck type name int*/ s = getsym(); if(s == S) goto bad; l = newname(s->name, -1, -1); s = getsym(); if(s == S) goto bad; ty = s->type; while((c = getnsc()) == '*') ty = typ(TIND, ty); unget(c); newprot(s, ty, "a", &l->prot); goto out; } /*#pragma varargck type O int*/ t = getquoted(); if(t == nil) goto bad; s = getsym(); if(s == S) goto bad; ty = s->type; while((c = getnsc()) == '*') ty = typ(TIND, ty); unget(c); newprot(s, ty, t, &tprot); goto out; bad: yyerror("syntax in #pragma varargck"); out: while(getnsc() != '\n') ; } Node* nextarg(Node *n, Node **a) { if(n == Z) { *a = Z; return Z; } if(n->op == OLIST) { *a = n->left; return n->right; } *a = n; return Z; } void checkargs(Node *nn, char *s, int pos) { Node *a, *n; Bits flag; Tprot *l; if(!debug['F']) return; n = nn; for(;;) { s = strchr(s, '%'); if(s == 0) { nextarg(n, &a); if(a != Z) warn(nn, "more arguments than format %T", a->type); return; } s++; flag = getflag(s); while(nstar > 0) { n = nextarg(n, &a); pos++; nstar--; if(a == Z) { warn(nn, "more format than arguments %s", lastfmt); return; } if(a->type == T) continue; if(!sametype(types[TINT], a->type) && !sametype(types[TUINT], a->type)) warn(nn, "format mismatch '*' in %s %T, arg %d", lastfmt, a->type, pos); } for(l=tprot; l; l=l->link) if(sametype(types[TVOID], l->type)) { if(beq(flag, l->flag)) { s++; goto loop; } } n = nextarg(n, &a); pos++; if(a == Z) { warn(nn, "more format than arguments %s", lastfmt); return; } if(a->type == 0) continue; for(l=tprot; l; l=l->link) if(sametype(a->type, l->type)) { /*print("checking %T/%ux %T/%ux\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/ if(beq(flag, l->flag)) goto loop; } warn(nn, "format mismatch %s %T, arg %d", lastfmt, a->type, pos); loop:; } } void dpcheck(Node *n) { char *s; Node *a, *b; Tname *l; Tprot *tl; int i, j; if(n == Z) return; b = n->left; if(b == Z || b->op != ONAME) return; s = b->sym->name; for(l=tname; l; l=l->link) if(strcmp(s, l->name) == 0) break; if(l == 0) return; if(l->count > 0) { // fetch count, then check remaining length i = l->count; a = nil; b = n->right; while(i > 0) { b = nextarg(b, &a); i--; } if(a == Z) { diag(n, "can't find count arg"); return; } if(a->op != OCONST || !typechl[a->type->etype]) { diag(n, "count is invalid constant"); return; } j = a->vconst; i = 0; while(b != Z) { b = nextarg(b, &a); i++; } if(i != j) diag(n, "found %d argument%s after count %d", i, i == 1 ? "" : "s", j); } if(l->prot != nil) { // check that all arguments after param or count // are listed in type list. i = l->count; if(i == 0) i = l->param; if(i == 0) return; a = nil; b = n->right; while(i > 0) { b = nextarg(b, &a); i--; } if(a == Z) { diag(n, "can't find count/param arg"); return; } while(b != Z) { b = nextarg(b, &a); for(tl=l->prot; tl; tl=tl->link) if(sametype(a->type, tl->type)) break; if(tl == nil) diag(a, "invalid type %T in call to %s", a->type, s); } } if(l->param <= 0) return; i = l->param; a = nil; b = n->right; while(i > 0) { b = nextarg(b, &a); i--; } if(a == Z) { diag(n, "can't find format arg"); return; } if(!sametype(indchar, a->type)) { diag(n, "format arg type %T", a->type); return; } if(a->op != OADDR || a->left->op != ONAME || a->left->sym != symstring) { /* warn(n, "format arg not constant string");*/ return; } s = a->left->cstring; checkargs(b, s, l->param); } void pragpack(void) { Sym *s; packflg = 0; s = getsym(); if(s) { packflg = atoi(s->name+1); if(strcmp(s->name, "on") == 0 || strcmp(s->name, "yes") == 0) packflg = 1; } while(getnsc() != '\n') ; if(debug['f']) if(packflg) print("%4d: pack %d\n", lineno, packflg); else print("%4d: pack off\n", lineno); } void pragfpround(void) { Sym *s; fproundflg = 0; s = getsym(); if(s) { fproundflg = atoi(s->name+1); if(strcmp(s->name, "on") == 0 || strcmp(s->name, "yes") == 0) fproundflg = 1; } while(getnsc() != '\n') ; if(debug['f']) if(fproundflg) print("%4d: fproundflg %d\n", lineno, fproundflg); else print("%4d: fproundflg off\n", lineno); } void pragtextflag(void) { Sym *s; s = getsym(); if(s == S) { textflag = getnsn(); } else { if(s->macro) { macexpand(s, symb); } if(symb[0] < '0' || symb[0] > '9') yyerror("pragma textflag not an integer"); textflag = atoi(symb); } while(getnsc() != '\n') ; if(debug['f']) print("%4d: textflag %d\n", lineno, textflag); } void pragdataflag(void) { Sym *s; s = getsym(); if(s == S) { dataflag = getnsn(); } else { if(s->macro) { macexpand(s, symb); } if(symb[0] < '0' || symb[0] > '9') yyerror("pragma dataflag not an integer"); dataflag = atoi(symb); } while(getnsc() != '\n') ; if(debug['f']) print("%4d: dataflag %d\n", lineno, dataflag); } void pragincomplete(void) { Sym *s; Type *t; int istag, w, et; istag = 0; s = getsym(); if(s == nil) goto out; et = 0; w = s->lexical; if(w == LSTRUCT) et = TSTRUCT; else if(w == LUNION) et = TUNION; if(et != 0){ s = getsym(); if(s == nil){ yyerror("missing struct/union tag in pragma incomplete"); goto out; } if(s->lexical != LNAME && s->lexical != LTYPE){ yyerror("invalid struct/union tag: %s", s->name); goto out; } dotag(s, et, 0); istag = 1; }else if(strcmp(s->name, "_off_") == 0){ debug['T'] = 0; goto out; }else if(strcmp(s->name, "_on_") == 0){ debug['T'] = 1; goto out; } t = s->type; if(istag) t = s->suetag; if(t == T) yyerror("unknown type %s in pragma incomplete", s->name); else if(!typesu[t->etype]) yyerror("not struct/union type in pragma incomplete: %s", s->name); else t->garb |= GINCOMPLETE; out: while(getnsc() != '\n') ; if(debug['f']) print("%s incomplete\n", s->name); } Sym* getimpsym(void) { int c; char *cp; c = getnsc(); if(isspace(c) || c == '"') { unget(c); return S; } for(cp = symb;;) { if(cp <= symb+NSYMB-4) *cp++ = c; c = getc(); if(c > 0 && !isspace(c) && c != '"') continue; unget(c); break; } *cp = 0; if(cp > symb+NSYMB-4) yyerror("symbol too large: %s", symb); return lookup(); } static int more(void) { int c; do c = getnsc(); while(c == ' ' || c == '\t'); unget(c); return c != '\n'; } void pragcgo(char *verb) { Sym *local, *remote; char *p; if(strcmp(verb, "cgo_dynamic_linker") == 0 || strcmp(verb, "dynlinker") == 0) { p = getquoted(); if(p == nil) goto err1; fmtprint(&pragcgobuf, "cgo_dynamic_linker %q\n", p); goto out; err1: yyerror("usage: #pragma cgo_dynamic_linker \"path\""); goto out; } if(strcmp(verb, "dynexport") == 0) verb = "cgo_export_dynamic"; if(strcmp(verb, "cgo_export_static") == 0 || strcmp(verb, "cgo_export_dynamic") == 0) { local = getimpsym(); if(local == nil) goto err2; if(!more()) { fmtprint(&pragcgobuf, "%s %q\n", verb, local->name); goto out; } remote = getimpsym(); if(remote == nil) goto err2; fmtprint(&pragcgobuf, "%s %q %q\n", verb, local->name, remote->name); goto out; err2: yyerror("usage: #pragma %s local [remote]", verb); goto out; } if(strcmp(verb, "cgo_import_dynamic") == 0 || strcmp(verb, "dynimport") == 0) { local = getimpsym(); if(local == nil) goto err3; if(!more()) { fmtprint(&pragcgobuf, "cgo_import_dynamic %q\n", local->name); goto out; } remote = getimpsym(); if(remote == nil) goto err3; if(!more()) { fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q\n", local->name, remote->name); goto out; } p = getquoted(); if(p == nil) goto err3; fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q %q\n", local->name, remote->name, p); goto out; err3: yyerror("usage: #pragma cgo_import_dynamic local [remote [\"library\"]]"); goto out; } if(strcmp(verb, "cgo_import_static") == 0) { local = getimpsym(); if(local == nil) goto err4; fmtprint(&pragcgobuf, "cgo_import_static %q\n", local->name); goto out; err4: yyerror("usage: #pragma cgo_import_static local [remote]"); goto out; } if(strcmp(verb, "cgo_ldflag") == 0) { p = getquoted(); if(p == nil) goto err5; fmtprint(&pragcgobuf, "cgo_ldflag %q\n", p); goto out; err5: yyerror("usage: #pragma cgo_ldflag \"arg\""); goto out; } out: while(getnsc() != '\n') ; }