summaryrefslogtreecommitdiff
path: root/src/cmd/cc/dpchk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/cc/dpchk.c')
-rw-r--r--src/cmd/cc/dpchk.c723
1 files changed, 723 insertions, 0 deletions
diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c
new file mode 100644
index 000000000..084aa0484
--- /dev/null
+++ b/src/cmd/cc/dpchk.c
@@ -0,0 +1,723 @@
+// 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 <u.h>
+#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;
+
+ textflag = 0;
+ s = getsym();
+ textflag = 7;
+ if(s)
+ textflag = atoi(s->name+1);
+ while(getnsc() != '\n')
+ ;
+ if(debug['f'])
+ print("%4d: textflag %d\n", lineno, textflag);
+}
+
+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();
+}
+
+void
+pragdynimport(void)
+{
+ Sym *local, *remote;
+ char *path;
+ Dynimp *f;
+
+ local = getimpsym();
+ if(local == nil)
+ goto err;
+
+ remote = getimpsym();
+ if(remote == nil)
+ goto err;
+
+ path = getquoted();
+ if(path == nil)
+ goto err;
+
+ if(ndynimp%32 == 0)
+ dynimp = realloc(dynimp, (ndynimp+32)*sizeof dynimp[0]);
+ f = &dynimp[ndynimp++];
+ f->local = local->name;
+ f->remote = remote->name;
+ f->path = path;
+ goto out;
+
+err:
+ yyerror("usage: #pragma dynimport local remote \"path\"");
+
+out:
+ while(getnsc() != '\n')
+ ;
+}
+
+void
+pragdynexport(void)
+{
+ Sym *local, *remote;
+ Dynexp *f;
+
+ local = getsym();
+ if(local == nil)
+ goto err;
+
+ remote = getsym();
+ if(remote == nil)
+ goto err;
+
+ if(ndynexp%32 == 0)
+ dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
+ f = &dynexp[ndynexp++];
+ f->local = local->name;
+ f->remote = remote->name;
+ goto out;
+
+err:
+ yyerror("usage: #pragma dynexport local remote");
+
+out:
+ while(getnsc() != '\n')
+ ;
+}