shithub: mc

Download patch

ref: 7088e222cf1b0ae17607ce0ac232067a172b851e
parent: 6b45f935bd240eb066fe4edd49b8cd0853bc5dd9
author: Ori Bernstein <[email protected]>
date: Tue Oct 7 16:17:58 EDT 2014

Keep track of source files as well as line numbers.

    This makes our error messages better *AND* makes it easier to
    provide good debug info later.

--- a/6/asm.h
+++ b/6/asm.h
@@ -198,7 +198,7 @@
 extern Loc **locmap; /* mapping from reg id => Loc * */
 
 char *genlblstr(char *buf, size_t sz);
-Node *genlbl(int line);
+Node *genlbl(Srcloc loc);
 Loc *loclbl(Node *lbl);
 Loc *locstrlbl(char *lbl);
 Loc *locreg(Mode m);
--- a/6/isel.c
+++ b/6/isel.c
@@ -1254,7 +1254,7 @@
             /* put in a comment that says where this line comes from */
             n = bb->nl[i];
             snprintf(buf, sizeof buf, "\n\t# bb = %ld, bbidx = %ld, %s:%d",
-                     j, i, file->file.files[n->fid], n->line);
+                     j, i, file->file.files[n->loc.file], n->loc.line);
             g(&is, Ilbl, locstrlbl(buf), NULL);
             isel(&is, fn->cfg->bb[j]->nl[i]);
         }
--- a/6/locs.c
+++ b/6/locs.c
@@ -79,12 +79,12 @@
     return buf;
 }
 
-Node *genlbl(int line)
+Node *genlbl(Srcloc loc)
 {
     char buf[128];
 
     genlblstr(buf, 128);
-    return mklbl(line, buf);
+    return mklbl(loc, buf);
 }
 
 Loc *locstrlbl(char *lbl)
--- a/6/simp.c
+++ b/6/simp.c
@@ -107,7 +107,7 @@
     Node *n;
 
     assert(size(a) == size(b));
-    n = mkexpr(a->line, Oadd, a, b, NULL);
+    n = mkexpr(a->loc, Oadd, a, b, NULL);
     n->expr.type = a->expr.type;
     return n;
 }
@@ -116,7 +116,7 @@
 {
     Node *k;
 
-    k = mkintlit(n->line, v);
+    k = mkintlit(n->loc, v);
     k->expr.type = exprtype(n);
     return add(n, k);
 }
@@ -125,7 +125,7 @@
 {
     Node *n;
 
-    n = mkexpr(a->line, Osub, a, b, NULL);
+    n = mkexpr(a->loc, Osub, a, b, NULL);
     n->expr.type = a->expr.type;
     return n;
 }
@@ -134,7 +134,7 @@
 {
     Node *k;
 
-    k = mkintlit(n->line, v);
+    k = mkintlit(n->loc, v);
     k->expr.type = exprtype(n);
     return sub(n, k);
 }
@@ -143,7 +143,7 @@
 {
     Node *n;
 
-    n = mkexpr(a->line, Omul, a, b, NULL);
+    n = mkexpr(a->loc, Omul, a, b, NULL);
     n->expr.type = a->expr.type;
     return n;
 }
@@ -209,13 +209,13 @@
 {
     Node *n;
 
-    n = mkexpr(a->line, Oaddr, a, NULL);
+    n = mkexpr(a->loc, Oaddr, a, NULL);
     if (!addressable(s, a))
             forcelocal(s, a);
     if (!bt)
-        n->expr.type = mktyptr(a->line, a->expr.type);
+        n->expr.type = mktyptr(a->loc, a->expr.type);
     else
-        n->expr.type = mktyptr(a->line, bt);
+        n->expr.type = mktyptr(a->loc, bt);
     return n;
 }
 
@@ -224,7 +224,7 @@
     Node *n;
 
     assert(a->expr.type->type == Typtr);
-    n = mkexpr(a->line, Oderef, a, NULL);
+    n = mkexpr(a->loc, Oderef, a, NULL);
     n->expr.type = base(a->expr.type);
     return n;
 }
@@ -234,7 +234,7 @@
     Node *n;
 
     assert(a->expr.type->type == Typtr);
-    n = mkexpr(a->line, Oderef, a, NULL);
+    n = mkexpr(a->loc, Oderef, a, NULL);
     if (t)
         n->expr.type = t;
     else
@@ -248,25 +248,25 @@
 
     assert(a != NULL && b != NULL);
     assert(exprop(a) == Ovar || exprop(a) == Oderef);
-    n = mkexpr(a->line, Oset, a, b, NULL);
+    n = mkexpr(a->loc, Oset, a, b, NULL);
     n->expr.type = exprtype(a);
     return n;
 }
 
-static Node *disp(int line, uint v)
+static Node *disp(Srcloc loc, uint v)
 {
     Node *n;
 
-    n = mkintlit(line, v);
+    n = mkintlit(loc, v);
     n->expr.type = tyintptr;
     return n;
 }
 
-static Node *word(int line, uint v)
+static Node *word(Srcloc loc, uint v)
 {
     Node *n;
 
-    n = mkintlit(line, v);
+    n = mkintlit(loc, v);
     n->expr.type = tyword;
     return n;
 }
@@ -426,9 +426,9 @@
     Node *t, *r, *n;
 
     snprintf(buf, 128, ".t%d", nexttmp++);
-    n = mkname(e->line, buf);
-    t = mkdecl(e->line, n, ty);
-    r = mkexpr(e->line, Ovar, n, NULL);
+    n = mkname(e->loc, buf);
+    t = mkdecl(e->loc, n, ty);
+    r = mkexpr(e->loc, Ovar, n, NULL);
     r->expr.type = t->decl.type;
     r->expr.did = t->decl.did;
     if (dcl)
@@ -449,7 +449,7 @@
 
 static void jmp(Simp *s, Node *lbl)
 {
-    append(s, mkexpr(lbl->line, Ojmp, lbl, NULL));
+    append(s, mkexpr(lbl->loc, Ojmp, lbl, NULL));
 }
 
 static void cjmp(Simp *s, Node *cond, Node *iftrue, Node *iffalse)
@@ -456,7 +456,7 @@
 {
     Node *jmp;
 
-    jmp = mkexpr(cond->line, Ocjmp, cond, iftrue, iffalse, NULL);
+    jmp = mkexpr(cond->loc, Ocjmp, cond, iftrue, iffalse, NULL);
     append(s, jmp);
 }
 
@@ -490,12 +490,12 @@
     Node *l1, *l2, *l3;
     Node *iftrue, *iffalse;
 
-    l1 = genlbl(n->line);
-    l2 = genlbl(n->line);
+    l1 = genlbl(n->loc);
+    l2 = genlbl(n->loc);
     if (exit)
         l3 = exit;
     else
-        l3 = genlbl(n->line);
+        l3 = genlbl(n->loc);
 
     iftrue = n->ifstmt.iftrue;
     iffalse = n->ifstmt.iffalse;
@@ -537,10 +537,10 @@
     Node *lcond;
     Node *lstep;
 
-    lbody = genlbl(n->line);
-    lcond = genlbl(n->line);
-    lstep = genlbl(n->line);
-    lend = genlbl(n->line);
+    lbody = genlbl(n->loc);
+    lcond = genlbl(n->loc);
+    lstep = genlbl(n->loc);
+    lend = genlbl(n->loc);
 
     lappend(&s->loopstep, &s->nloopstep, lstep);
     lappend(&s->loopexit, &s->nloopexit, lend);
@@ -583,16 +583,16 @@
     Node *idx, *len, *dcl, *seq, *val, *done;
     Node *zero;
 
-    lbody = genlbl(n->line);
-    lstep = genlbl(n->line);
-    lcond = genlbl(n->line);
-    lmatch = genlbl(n->line);
-    lend = genlbl(n->line);
+    lbody = genlbl(n->loc);
+    lstep = genlbl(n->loc);
+    lcond = genlbl(n->loc);
+    lmatch = genlbl(n->loc);
+    lend = genlbl(n->loc);
 
     lappend(&s->loopstep, &s->nloopstep, lstep);
     lappend(&s->loopexit, &s->nloopexit, lend);
 
-    zero = mkintlit(n->line, 0);
+    zero = mkintlit(n->loc, 0);
     zero->expr.type = tyintptr;
 
     seq = rval(s, n->iterstmt.seq, NULL);
@@ -611,7 +611,7 @@
     /* condition */
     simp(s, lcond);
     len = seqlen(s, seq, tyintptr);
-    done = mkexpr(n->line, Olt, idx, len, NULL);
+    done = mkexpr(n->loc, Olt, idx, len, NULL);
     cjmp(s, done, lmatch, lend);
     simp(s, lmatch);
     val = load(idxaddr(s, seq, idx));
@@ -645,10 +645,10 @@
     Ucon *uc;
 
     if (exprop(n) != Oucon)
-        return load(addr(s, n, mktype(n->line, Tyuint)));
+        return load(addr(s, n, mktype(n->loc, Tyuint)));
 
     uc = finducon(n);
-    return word(uc->line, uc->id);
+    return word(uc->loc, uc->id);
 }
 
 static Node *patval(Simp *s, Node *n, Type *t)
@@ -694,24 +694,24 @@
             str = lit->lit.strval;
 
             /* load slice length */
-            next = genlbl(pat->line);
+            next = genlbl(pat->loc);
             x = slicelen(s, val);
             len = strlen(str);
-            y = mkintlit(lit->line, len);
+            y = mkintlit(lit->loc, len);
             y->expr.type = tyintptr;
-            v = mkexpr(pat->line, Oeq, x, y, NULL);
+            v = mkexpr(pat->loc, Oeq, x, y, NULL);
             cjmp(s, v, next, iffalse);
             append(s, next);
 
             for (i = 0; i < len; i++) {
-                next = genlbl(pat->line);
-                x = mkintlit(pat->line, str[i]);
-                x->expr.type = mktype(pat->line, Tybyte);
-                idx = mkintlit(pat->line, i);
+                next = genlbl(pat->loc);
+                x = mkintlit(pat->loc, str[i]);
+                x->expr.type = mktype(pat->loc, Tybyte);
+                idx = mkintlit(pat->loc, i);
                 idx->expr.type = tyintptr;
                 y = load(idxaddr(s, val, idx));
-                v = mkexpr(pat->line, Oeq, x, y, NULL);
-                v->expr.type = mktype(pat->line, Tybool);
+                v = mkexpr(pat->loc, Oeq, x, y, NULL);
+                v->expr.type = mktype(pat->loc, Tybool);
                 cjmp(s, v, next, iffalse);
                 append(s, next);
             }
@@ -723,8 +723,8 @@
         case Tyint64: case Tyuint64: case Tylong:  case Tyulong:
         case Tyflt32: case Tyflt64:
         case Typtr: case Tyfunc:
-            v = mkexpr(pat->line, Oeq, pat, val, NULL);
-            v->expr.type = mktype(pat->line, Tybool);
+            v = mkexpr(pat->loc, Oeq, pat, val, NULL);
+            v->expr.type = mktype(pat->loc, Tybool);
             cjmp(s, v, iftrue, iffalse);
             break;
         /* We got lucky. The structure of tuple, array, and struct literals
@@ -735,7 +735,7 @@
             off = 0;
             for (i = 0; i < pat->expr.nargs; i++) {
                 off = alignto(off, exprtype(patarg[i]));
-                next = genlbl(pat->line);
+                next = genlbl(pat->loc);
                 v = load(addk(addr(s, val, exprtype(patarg[i])), off));
                 matchpattern(s, patarg[i], v, exprtype(patarg[i]), next, iffalse);
                 append(s, next);
@@ -747,7 +747,7 @@
             patarg = pat->expr.args;
             for (i = 0; i < pat->expr.nargs; i++) {
                 off = offset(pat, patarg[i]->expr.idx);
-                next = genlbl(pat->line);
+                next = genlbl(pat->loc);
                 v = load(addk(addr(s, val, exprtype(patarg[i])), off));
                 matchpattern(s, patarg[i], v, exprtype(patarg[i]), next, iffalse);
                 append(s, next);
@@ -758,11 +758,11 @@
             if (!uc)
                 uc = finducon(val);
 
-            deeper = genlbl(pat->line);
+            deeper = genlbl(pat->loc);
 
             x = uconid(s, pat);
             y = uconid(s, val);
-            v = mkexpr(pat->line, Oeq, x, y, NULL);
+            v = mkexpr(pat->loc, Oeq, x, y, NULL);
             v->expr.type = tyintptr;
             cjmp(s, v, deeper, iffalse);
             append(s, deeper);
@@ -782,7 +782,7 @@
     Node *m;
     size_t i;
 
-    end = genlbl(n->line);
+    end = genlbl(n->loc);
     val = temp(s, n->matchstmt.val);
     tmp = rval(s, n->matchstmt.val, val);
     if (val != tmp)
@@ -791,8 +791,8 @@
         m = n->matchstmt.matches[i];
 
         /* check pattern */
-        cur = genlbl(n->line);
-        next = genlbl(n->line);
+        cur = genlbl(n->loc);
+        next = genlbl(n->loc);
         matchpattern(s, m->match.pat, val, val->expr.type, cur, next);
 
         /* do the action if it matches */
@@ -821,9 +821,9 @@
     Node *n, *d, *r;
     char lbl[128];
 
-    n = mkname(blob->line, genlblstr(lbl, 128));
-    d = mkdecl(blob->line, n, blob->expr.type);
-    r = mkexpr(blob->line, Ovar, n, NULL);
+    n = mkname(blob->loc, genlblstr(lbl, 128));
+    d = mkdecl(blob->loc, n, blob->expr.type);
+    r = mkexpr(blob->loc, Ovar, n, NULL);
 
     d->decl.init = blob;
     d->decl.type = blob->expr.type;
@@ -843,9 +843,9 @@
     if (size(v) == Ptrsz)
         return v;
     else if (size(v) < Ptrsz)
-        v = mkexpr(v->line, Ozwiden, v, NULL);
+        v = mkexpr(v->loc, Ozwiden, v, NULL);
     else if (size(v) > Ptrsz)
-        v = mkexpr(v->line, Otrunc, v, NULL);
+        v = mkexpr(v->loc, Otrunc, v, NULL);
     v->expr.type = tyintptr;
     return v;
 }
@@ -863,9 +863,9 @@
     } else {
         t = addr(s, lval(s, args[0]), exprtype(n));
     }
-    u = disp(n->line, offset(args[0], args[1]));
+    u = disp(n->loc, offset(args[0], args[1]));
     r = add(t, u);
-    r->expr.type = mktyptr(n->line, n->expr.type);
+    r->expr.type = mktyptr(n->loc, n->expr.type);
     return r;
 }
 
@@ -875,12 +875,12 @@
     Node *ok, *fail;
 
     /* create expressions */
-    cmp = mkexpr(idx->line, Olt, ptrsized(s, idx), ptrsized(s, len), NULL);
-    cmp->expr.type = mktype(len->line, Tybool);
-    ok = genlbl(len->line);
-    fail = genlbl(len->line);
-    die = mkexpr(idx->line, Ocall, abortfunc, NULL);
-    die->expr.type = mktype(len->line, Tyvoid);
+    cmp = mkexpr(idx->loc, Olt, ptrsized(s, idx), ptrsized(s, len), NULL);
+    cmp->expr.type = mktype(len->loc, Tybool);
+    ok = genlbl(len->loc);
+    fail = genlbl(len->loc);
+    die = mkexpr(idx->loc, Ocall, abortfunc, NULL);
+    die->expr.type = mktype(len->loc, Tyvoid);
 
     /* insert them */
     cjmp(s, cmp, ok, fail);
@@ -902,7 +902,7 @@
         t = addr(s, a, ty);
         w = exprtype(a)->asize;
     } else if (seq->expr.type->type == Tyslice) {
-        t = load(addr(s, a, mktyptr(seq->line, ty)));
+        t = load(addr(s, a, mktyptr(seq->loc, ty)));
         w = slicelen(s, a);
     } else {
         die("Can't index type %s\n", tystr(seq->expr.type));
@@ -912,7 +912,7 @@
     u = ptrsized(s, u);
     checkidx(s, w, u);
     sz = tysize(ty);
-    v = mul(u, disp(seq->line, sz));
+    v = mul(u, disp(seq->loc, sz));
     r = add(t, v);
     return r;
 }
@@ -929,7 +929,7 @@
     switch (ty->type) {
         case Typtr:     u = t; break;
         case Tyarray:   u = addr(s, t, base(exprtype(n))); break;
-        case Tyslice:   u = load(addr(s, t, mktyptr(n->line, base(exprtype(n))))); break;
+        case Tyslice:   u = load(addr(s, t, mktyptr(n->loc, base(exprtype(n))))); break;
         default: die("Unslicable type %s", tystr(n->expr.type));
     }
     /* safe: all types we allow here have a sub[0] that we want to grab */
@@ -936,7 +936,7 @@
     if (off) {
       off = ptrsized(s, rval(s, off, NULL));
       sz = tysize(n->expr.type->sub[0]);
-      v = mul(off, disp(n->line, sz));
+      v = mul(off, disp(n->loc, sz));
       return add(u, v);
     } else {
       return u;
@@ -969,13 +969,13 @@
     args = n->expr.args;
     switch (exprop(n)) {
         case Oland:
-            lnext = genlbl(n->line);
+            lnext = genlbl(n->loc);
             simpcond(s, args[0], lnext, lfalse);
             append(s, lnext);
             simpcond(s, args[1], ltrue, lfalse);
             break;
         case Olor:
-            lnext = genlbl(n->line);
+            lnext = genlbl(n->loc);
             simpcond(s, args[0], ltrue, lnext);
             append(s, lnext);
             simpcond(s, args[1], ltrue, lfalse);
@@ -999,12 +999,12 @@
     tosz = tysize(to);
     r = rval(s, from, NULL);
     if (fromsz > tosz) {
-        r = mkexpr(from->line, Otrunc, r, NULL);
+        r = mkexpr(from->loc, Otrunc, r, NULL);
     } else if (tosz > fromsz) {
         if (issigned)
-            r = mkexpr(from->line, Oswiden, r, NULL);
+            r = mkexpr(from->loc, Oswiden, r, NULL);
         else
-            r = mkexpr(from->line, Ozwiden, r, NULL);
+            r = mkexpr(from->loc, Ozwiden, r, NULL);
     }
     r->expr.type = to;
     return r;
@@ -1049,7 +1049,7 @@
                     if (tybase(to)->type == Typtr)
                         fatal(val, "Bad cast from %s to %s",
                               tystr(exprtype(val)), tystr(to));
-                    r = mkexpr(val->line, Oflt2int, rval(s, val, NULL), NULL);
+                    r = mkexpr(val->loc, Oflt2int, rval(s, val, NULL), NULL);
                     r->expr.type = to;
                     break;
                 default:
@@ -1064,11 +1064,11 @@
                 case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64:
                 case Tyint: case Tyuint: case Tylong: case Tyulong:
                 case Tychar: case Tybyte:
-                    r = mkexpr(val->line, Oflt2int, rval(s, val, NULL), NULL);
+                    r = mkexpr(val->loc, Oflt2int, rval(s, val, NULL), NULL);
                     r->expr.type = to;
                     break;
                 case Tyflt32: case Tyflt64:
-                    r = mkexpr(val->line, Oflt2flt, rval(s, val, NULL), NULL);
+                    r = mkexpr(val->loc, Oflt2flt, rval(s, val, NULL), NULL);
                     r->expr.type = to;
                     break;
                 default:
@@ -1106,8 +1106,8 @@
     /* we can be storing through a pointer, in the case
      * of '*foo = bar'. */
     if (tybase(exprtype(t))->type == Typtr) {
-        stbase = set(simpcast(s, t, mktyptr(t->line, tyintptr)), base);
-        sz = addk(simpcast(s, t, mktyptr(t->line, tyintptr)), Ptrsz);
+        stbase = set(simpcast(s, t, mktyptr(t->loc, tyintptr)), base);
+        sz = addk(simpcast(s, t, mktyptr(t->loc, tyintptr)), Ptrsz);
     } else {
         stbase = set(deref(addr(s, t, tyintptr), NULL), base);
         sz = addk(addr(s, t, tyintptr), Ptrsz);
@@ -1153,11 +1153,11 @@
     for (i = 0; i < lhs->expr.nargs; i++) {
         lv = lval(s, args[i]);
         off = alignto(off, exprtype(lv));
-        prv = add(addr(s, rhs, exprtype(args[i])), disp(rhs->line, off));
+        prv = add(addr(s, rhs, exprtype(args[i])), disp(rhs->loc, off));
         if (stacknode(args[i])) {
-            sz = disp(lhs->line, size(lv));
+            sz = disp(lhs->loc, size(lv));
             plv = addr(s, lv, exprtype(lv));
-            stor = mkexpr(lhs->line, Oblit, plv, prv, sz, NULL);
+            stor = mkexpr(lhs->loc, Oblit, plv, prv, sz, NULL);
         } else {
             stor = set(lv, load(prv));
         }
@@ -1185,8 +1185,8 @@
         } else if (stacknode(lhs)) {
             t = addr(s, t, exprtype(lhs));
             u = addr(s, u, exprtype(lhs));
-            v = disp(lhs->line, size(lhs));
-            r = mkexpr(lhs->line, Oblit, t, u, v, NULL);
+            v = disp(lhs->loc, size(lhs));
+            r = mkexpr(lhs->loc, Oblit, t, u, v, NULL);
         } else {
             r = set(t, u);
         }
@@ -1201,12 +1201,12 @@
     Node *st;
 
     val = rval(s, val, NULL);
-    pdst = add(r, disp(val->line, off));
+    pdst = add(r, disp(val->loc, off));
 
     if (stacknode(val)) {
-        sz = disp(val->line, size(val));
+        sz = disp(val->loc, size(val));
         pval = addr(s, val, exprtype(val));
-        st = mkexpr(val->line, Oblit, pdst, pval, sz, NULL);
+        st = mkexpr(val->loc, Oblit, pdst, pval, sz, NULL);
     } else {
         st = set(deref(pdst, val->expr.type), val);
     }
@@ -1264,9 +1264,9 @@
         tmp = temp(s, n);
 
     /* Set the tag on the ucon */
-    u = addr(s, tmp, mktype(n->line, Tyuint));
-    tag = mkintlit(n->line, uc->id);
-    tag->expr.type = mktype(n->line, Tyuint);
+    u = addr(s, tmp, mktype(n->loc, Tyuint));
+    tag = mkintlit(n->loc, uc->id);
+    tag->expr.type = mktype(n->loc, Tyuint);
     append(s, set(deref(u, tyword), tag));
 
 
@@ -1277,8 +1277,8 @@
     u = addk(u, Wordsz);
     if (stacktype(uc->etype)) {
         elt = addr(s, elt, uc->etype);
-        sz = disp(n->line, tysize(uc->etype));
-        r = mkexpr(n->line, Oblit, u, elt, sz, NULL);
+        sz = disp(n->loc, tysize(uc->etype));
+        r = mkexpr(n->loc, Oblit, u, elt, sz, NULL);
     } else {
         r = set(deref(u, uc->etype), elt);
     }
@@ -1308,9 +1308,9 @@
 
     /* set up temps and labels */
     r = temp(s, n);
-    ltrue = genlbl(n->line);
-    lfalse = genlbl(n->line);
-    ldone = genlbl(n->line);
+    ltrue = genlbl(n->loc);
+    lfalse = genlbl(n->loc);
+    ldone = genlbl(n->loc);
 
     /* simp the conditional */
     simpcond(s, n, ltrue, lfalse);
@@ -1317,8 +1317,8 @@
 
     /* if true */
     append(s, ltrue);
-    u = mkexpr(n->line, Olit, mkbool(n->line, 1), NULL);
-    u->expr.type = mktype(n->line, Tybool);
+    u = mkexpr(n->loc, Olit, mkbool(n->loc, 1), NULL);
+    u->expr.type = mktype(n->loc, Tybool);
     t = set(r, u);
     append(s, t);
     jmp(s, ldone);
@@ -1325,8 +1325,8 @@
 
     /* if false */
     append(s, lfalse);
-    u = mkexpr(n->line, Olit, mkbool(n->line, 0), NULL);
-    u->expr.type = mktype(n->line, Tybool);
+    u = mkexpr(n->loc, Olit, mkbool(n->loc, 0), NULL);
+    u->expr.type = mktype(n->loc, Tybool);
     t = set(r, u);
     append(s, t);
     jmp(s, ldone);
@@ -1401,7 +1401,7 @@
             r = simplazy(s, n);
             break;
         case Osize:
-            r = mkintlit(n->line, size(args[0]));
+            r = mkintlit(n->loc, size(args[0]));
             r->expr.type = exprtype(n);
             break;
         case Oslice:
@@ -1459,7 +1459,7 @@
             assert(fusedmap[exprop(n)] != Obad);
             u = rval(s, args[0], NULL);
             v = rval(s, args[1], NULL);
-            v = mkexpr(n->line, fusedmap[exprop(n)], u, v, NULL);
+            v = mkexpr(n->loc, fusedmap[exprop(n)], u, v, NULL);
             v->expr.type = u->expr.type;
             r = set(u, v);
             break;
@@ -1520,8 +1520,8 @@
             if (s->isbigret) {
                 t = rval(s, args[0], NULL);
                 t = addr(s, t, exprtype(args[0]));
-                u = disp(n->line, size(args[0]));
-                v = mkexpr(n->line, Oblit, s->ret, t, u, NULL);
+                u = disp(n->loc, size(args[0]));
+                v = mkexpr(n->loc, Oblit, s->ret, t, u, NULL);
                 append(s, v);
             } else if (n->expr.nargs && n->expr.args[0]) {
                 t = s->ret;
@@ -1532,7 +1532,7 @@
             for (i = 0; i < s->nqueue; i++)
                 append(s, s->incqueue[i]);
             lfree(&s->incqueue, &s->nqueue);
-            append(s, mkexpr(n->line, Oret, NULL));
+            append(s, mkexpr(n->loc, Oret, NULL));
             break;
         case Oasn:
             r = assign(s, args[0], args[1]);
@@ -1560,12 +1560,12 @@
             break;
         case Oneg:
             if (istyfloat(exprtype(n))) {
-                t =mkfloat(n->line, -1.0); 
-                u = mkexpr(n->line, Olit, t, NULL);
+                t =mkfloat(n->loc, -1.0); 
+                u = mkexpr(n->loc, Olit, t, NULL);
                 t->lit.type = n->expr.type;
                 u->expr.type = n->expr.type;
                 v = simpblob(s, u, &s->blobs, &s->nblobs);
-                r = mkexpr(n->line, Ofmul, v, rval(s, args[0], NULL), NULL);
+                r = mkexpr(n->loc, Ofmul, v, rval(s, args[0], NULL), NULL);
                 r->expr.type = n->expr.type;
             } else {
                 r = visit(s, n);
@@ -1658,8 +1658,8 @@
             declarelocal(s, n);
             if (!n->decl.init)
                 break;
-            t = mkexpr(n->line, Ovar, n->decl.name, NULL);
-            u = mkexpr(n->line, Oasn, t, n->decl.init, NULL);
+            t = mkexpr(n->loc, Ovar, n->decl.name, NULL);
+            u = mkexpr(n->loc, Oasn, t, n->decl.init, NULL);
             u->expr.type = n->decl.type;
             t->expr.type = n->decl.type;
             t->expr.did = n->decl.did;
@@ -1686,7 +1686,7 @@
     assert(f->type == Nfunc);
     s->nstmts = 0;
     s->stmts = NULL;
-    s->endlbl = genlbl(f->line);
+    s->endlbl = genlbl(f->loc);
     s->ret = NULL;
 
     /* make a temp for the return type */
@@ -1693,7 +1693,7 @@
     ty = f->func.type->sub[0];
     if (stacktype(ty)) {
         s->isbigret = 1;
-        s->ret = gentemp(s, f, mktyptr(f->line, ty), &dcl);
+        s->ret = gentemp(s, f, mktyptr(f->loc, ty), &dcl);
         declarearg(s, dcl);
     } else if (ty->type != Tyvoid) {
         s->isbigret = 0;
@@ -1867,18 +1867,18 @@
     Node *name;
     Node *dcl;
 
-    tyintptr = mktype(-1, Tyuint64);
-    tyword = mktype(-1, Tyuint);
-    tyvoid = mktype(-1, Tyvoid);
+    tyintptr = mktype(Zloc, Tyuint64);
+    tyword = mktype(Zloc, Tyuint);
+    tyvoid = mktype(Zloc, Tyvoid);
 
-    ty = mktyfunc(-1, NULL, 0, mktype(-1, Tyvoid));
-    name = mknsname(-1, "_rt", "abort_oob");
-    dcl = mkdecl(-1, name, ty);
+    ty = mktyfunc(Zloc, NULL, 0, mktype(Zloc, Tyvoid));
+    name = mknsname(Zloc, "_rt", "abort_oob");
+    dcl = mkdecl(Zloc, name, ty);
     dcl->decl.isconst = 1;
     dcl->decl.isextern = 1;
     htput(globls, dcl, asmname(dcl->decl.name));
 
-    abortfunc = mkexpr(-1, Ovar, name, NULL);
+    abortfunc = mkexpr(Zloc, Ovar, name, NULL);
     abortfunc->expr.type = ty;
     abortfunc->expr.did = dcl->decl.did;
     abortfunc->expr.isconst = 1;
--- a/opt/cfg.c
+++ b/opt/cfg.c
@@ -92,12 +92,12 @@
     return 1;
 }
 
-static Bb *addlabel(Cfg *cfg, Bb *bb, Node **nl, size_t i, int line)
+static Bb *addlabel(Cfg *cfg, Bb *bb, Node **nl, size_t i, Srcloc loc)
 {
     /* if the current block assumes fall-through, insert an explicit jump */
     if (i > 0 && nl[i - 1]->type == Nexpr) {
         if (exprop(nl[i - 1]) != Ocjmp && exprop(nl[i - 1]) != Ojmp)
-            addnode(cfg, bb, mkexpr(line, Ojmp, mklbl(line, lblstr(nl[i])), NULL));
+            addnode(cfg, bb, mkexpr(loc, Ojmp, mklbl(loc, lblstr(nl[i])), NULL));
     }
     if (bb->nnl)
         bb = mkbb(cfg);
@@ -203,7 +203,7 @@
         switch (nl[i]->type) {
             case Nexpr:
                 if (islabel(nl[i]))
-                    bb = addlabel(cfg, bb, nl, i, nl[i]->line);
+                    bb = addlabel(cfg, bb, nl, i, nl[i]->loc);
                 else if (addnode(cfg, bb, nl[i]))
                     bb = mkbb(cfg);
                 break;
@@ -235,7 +235,7 @@
                 b = cfg->fixjmp[i]->expr.args[2];
                 break;
             case Oret:
-                a = mklbl(cfg->fixjmp[i]->line, cfg->end->lbls[0]);
+                a = mklbl(cfg->fixjmp[i]->loc, cfg->end->lbls[0]);
                 b = NULL;
                 break;
             default:
--- a/opt/fold.c
+++ b/opt/fold.c
@@ -35,12 +35,12 @@
     return v == val;
 }
 
-static Node *val(int line, vlong val, Type *t)
+static Node *val(Srcloc loc, vlong val, Type *t)
 {
     Node *l, *n;
 
-    l = mkint(line, val);
-    n = mkexpr(line, Olit, l, NULL);
+    l = mkint(loc, val);
+    n = mkexpr(loc, Olit, l, NULL);
     l->lit.type = t;
     n->expr.type = t;
     return n;
@@ -128,7 +128,7 @@
             if (isval(args[1], 0))
                 r = args[0];
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a + b, exprtype(n));
+                r = val(n->loc, a + b, exprtype(n));
             break;
         case Osub:
             /* x - 0 = 0 */
@@ -135,7 +135,7 @@
             if (isval(args[1], 0))
                 r = args[0];
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a - b, exprtype(n));
+                r = val(n->loc, a - b, exprtype(n));
             break;
         case Omul:
             /* 1 * x = x */
@@ -149,7 +149,7 @@
             if (isval(args[1], 0))
                 r = args[1];
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a * b, exprtype(n));
+                r = val(n->loc, a * b, exprtype(n));
             break;
         case Odiv:
             /* x/1 = x */
@@ -159,7 +159,7 @@
             if (isval(args[1], 0))
                 r = args[1];
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a / b, exprtype(n));
+                r = val(n->loc, a / b, exprtype(n));
             break;
         case Omod:
             /* x%1 = x */
@@ -166,31 +166,31 @@
             if (isval(args[1], 0))
                 r = args[0];
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a % b, exprtype(n));
+                r = val(n->loc, a % b, exprtype(n));
             break;
         case Oneg:
             if (islit(args[0], &a))
-                r = val(n->line, -a, exprtype(n));
+                r = val(n->loc, -a, exprtype(n));
             break;
         case Obsl:
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a << b, exprtype(n));
+                r = val(n->loc, a << b, exprtype(n));
             break;
         case Obsr:
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a >> b, exprtype(n));
+                r = val(n->loc, a >> b, exprtype(n));
             break;
         case Obor:
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a | b, exprtype(n));
+                r = val(n->loc, a | b, exprtype(n));
             break;
         case Oband:
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a & b, exprtype(n));
+                r = val(n->loc, a & b, exprtype(n));
             break;
         case Obxor:
             if (islit(args[0], &a) && islit(args[1], &b))
-                r = val(n->line, a ^ b, exprtype(n));
+                r = val(n->loc, a ^ b, exprtype(n));
             break;
         case Omemb:
             t = tybase(exprtype(args[0]));
--- a/parse/dump.c
+++ b/parse/dump.c
@@ -110,7 +110,7 @@
         findentf(fd, depth, "Nil\n");
         return;
     }
-    findentf(fd, depth, "%s.%zd@%i", nodestr(n->type), n->nid, n->line);
+    findentf(fd, depth, "%s.%zd@%i", nodestr(n->type), n->nid, n->loc.line);
     switch(n->type) {
         case Nfile:
             fprintf(fd, "(name = %s)\n", n->file.files[0]);
--- a/parse/gram.y
+++ b/parse/gram.y
@@ -159,7 +159,7 @@
 
 %union {
     struct {
-        int line;
+        Srcloc loc;
         Node **nl;
         size_t nn;
     } nodelist;
@@ -168,17 +168,17 @@
         size_t nstr;
     } strlist;
     struct {
-        int line;
+        Srcloc loc;
         Ucon **ucl;
         size_t nucl;
     } uconlist;
     struct {
-        int line;
+        Srcloc loc;
         Type **types;
         size_t ntypes;
     } tylist;
     struct { /* FIXME: unused */
-        int line;
+        Srcloc loc;
         char *name;
         Type *type;
         Type **params;
@@ -210,7 +210,7 @@
                     putdcl(file->file.exports, $1->funcs[i]);
             }
         | tydef {
-                puttype(file->file.globls, mkname($1.line, $1.name), $1.type);
+                puttype(file->file.globls, mkname($1.loc, $1.name), $1.type);
                 installucons(file->file.globls, $1.type);
             }
         | decl {
@@ -267,8 +267,8 @@
             }
         ;
 
-use     : Tuse Tident {$$ = mkuse($1->line, $2->str, 0);}
-        | Tuse Tstrlit {$$ = mkuse($1->line, $2->str, 1);}
+use     : Tuse Tident {$$ = mkuse($1->loc, $2->str, 0);}
+        | Tuse Tstrlit {$$ = mkuse($1->loc, $2->str, 1);}
         ;
 
 optident: Tident      {$$ = $1;}
@@ -277,7 +277,7 @@
 
 package : Tpkg optident Tasn pkgbody Tendblk {
                 if (file->file.exports->name)
-                    lfatal($1->line, 0, "Package already declared\n");
+                    lfatal($1->loc, "Package already declared\n");
                 if ($2) {
                     updatens(file->file.exports, $2->str);
                     updatens(file->file.globls, $2->str);
@@ -298,7 +298,7 @@
                 }
             }
         | pkgtydef {
-                puttype(file->file.exports, mkname($1.line, $1.name), $1.type);
+                puttype(file->file.exports, mkname($1.loc, $1.name), $1.type);
                 installucons(file->file.exports, $1.type);
             }
         | traitdef {
@@ -322,7 +322,7 @@
                     if (!strcmp($1.str[i], "pkglocal"))
                         $$.type->ispkglocal = 1;
                     else
-                        lfatal($$.line, 0, "invalid type attribute '%s'", $1.str[i]);
+                        lfatal($$.loc, "invalid type attribute '%s'", $1.str[i]);
                 }
             }
         ;
@@ -331,24 +331,24 @@
         | declcore
         ;
 
-declcore: name {$$ = mkdecl($1->line, $1, mktyvar($1->line));}
+declcore: name {$$ = mkdecl($1->loc, $1, mktyvar($1->loc));}
         | typedeclcore {$$ = $1;}
         ;
 
 typedeclcore
-        : name Tcolon type {$$ = mkdecl($1->line, $1, $3);}
+        : name Tcolon type {$$ = mkdecl($1->loc, $1, $3);}
         ;
 
-name    : Tident {$$ = mkname($1->line, $1->str);}
+name    : Tident {$$ = mkname($1->loc, $1->str);}
         | Tident Tdot name {$$ = $3; setns($3, $1->str);}
         ;
 
 implstmt: Timpl name type {
-                $$ = mkimplstmt($1->line, $2, $3, NULL, 0);
+                $$ = mkimplstmt($1->loc, $2, $3, NULL, 0);
                 $$->impl.isproto = 1;
             }
         | Timpl name type Tasn Tendln implbody Tendblk {
-                $$ = mkimplstmt($1->line, $2, $3, $6.nl, $6.nn);
+                $$ = mkimplstmt($1->loc, $2, $3, $6.nl, $6.nn);
             }
         ;
 
@@ -357,7 +357,7 @@
         | implbody Tident Tasn exprln optendlns {
                 Node *d;
                 $$ = $1;
-                d = mkdecl($2->line, mkname($2->line, $2->str), mktyvar($2->line));
+                d = mkdecl($2->loc, mkname($2->loc, $2->str), mktyvar($2->loc));
                 d->decl.init = $4;
                 d->decl.isconst = 1;
                 lappend(&$$.nl, &$$.nn, d);
@@ -365,11 +365,11 @@
         ;
 
 traitdef: Ttrait Tident generictype /* trait prototype */ {
-                $$ = mktrait($1->line, mkname($2->line, $2->str), $3, NULL, 0, NULL, 0, 1);
+                $$ = mktrait($1->loc, mkname($2->loc, $2->str), $3, NULL, 0, NULL, 0, 1);
             }
         | Ttrait Tident generictype Tasn traitbody Tendblk /* trait definition */ {
                 size_t i;
-                $$ = mktrait($1->line, mkname($2->line, $2->str), $3, NULL, 0, $5.nl, $5.nn, 0);
+                $$ = mktrait($1->loc, mkname($2->loc, $2->str), $3, NULL, 0, $5.nl, $5.nn, 0);
                 for (i = 0; i < $5.nn; i++) {
                     $5.nl[i]->decl.trait = $$;
                     $5.nl[i]->decl.isgeneric = 1;
@@ -382,7 +382,7 @@
         | traitbody Tident Tcolon type optendlns {
                 Node *d;
                 $$ = $1;
-                d = mkdecl($2->line, mkname($2->line, $2->str), $4);
+                d = mkdecl($2->loc, mkname($2->loc, $2->str), $4);
                 d->decl.isgeneric = 1;
                 lappend(&$$.nl, &$$.nn, d);
             }
@@ -392,18 +392,18 @@
 tydef   : Ttype typeid {$$ = $2;}
         | Ttype typeid Tasn type {
                 $$ = $2;
-                $$.type = mktyname($2.line, mkname($2.line, $2.name), $2.params, $2.nparams, $4);
+                $$.type = mktyname($2.loc, mkname($2.loc, $2.name), $2.params, $2.nparams, $4);
             }
         ;
 
 typeid  : Tident {
-                $$.line = $1->line;
+                $$.loc = $1->loc;
                 $$.name = $1->str;
                 $$.params = NULL;
                 $$.type = NULL;
             }
         | Tident Toparen typarams Tcparen {
-                $$.line = $1->line;
+                $$.loc = $1->loc;
                 $$.name = $1->str;
                 $$.params = $3.types;
                 $$.nparams = $3.ntypes;
@@ -423,18 +423,18 @@
         | uniondef
         | compoundtype
         | generictype
-        | Tellipsis {$$ = mktype($1->line, Tyvalist);}
+        | Tellipsis {$$ = mktype($1->loc, Tyvalist);}
         ;
 
 generictype
-        : Ttyparam {$$ = mktyparam($1->line, $1->str);}
+        : Ttyparam {$$ = mktyparam($1->loc, $1->str);}
         | Ttyparam Twith name {
-                $$ = mktyparam($1->line, $1->str);
+                $$ = mktyparam($1->loc, $1->str);
                 addtrait($$, $3->name.name);
             }
         | Ttyparam Twith Toparen typaramlist Tcparen {
                 size_t i;
-                $$ = mktyparam($1->line, $1->str);
+                $$ = mktyparam($1->loc, $1->str);
                 for (i = 0; i < $4.nn; i++)
                     addtrait($$, $4.nl[i]->name.name);
             }
@@ -450,12 +450,12 @@
 
 compoundtype
         : functype   {$$ = $1;}
-        | type Tosqbrac Tcolon Tcsqbrac {$$ = mktyslice($2->line, $1);}
-        | type Tosqbrac expr Tcsqbrac {$$ = mktyarray($2->line, $1, $3);}
-        | type Tderef {$$ = mktyptr($2->line, $1);}
-        | Tat Tident {$$ = mktyparam($1->line, $2->str);}
-        | name       {$$ = mktyunres($1->line, $1, NULL, 0);}
-        | name Toparen typelist Tcparen {$$ = mktyunres($1->line, $1, $3.types, $3.ntypes);}
+        | type Tosqbrac Tcolon Tcsqbrac {$$ = mktyslice($2->loc, $1);}
+        | type Tosqbrac expr Tcsqbrac {$$ = mktyarray($2->loc, $1, $3);}
+        | type Tderef {$$ = mktyptr($2->loc, $1);}
+        | Tat Tident {$$ = mktyparam($1->loc, $2->str);}
+        | name       {$$ = mktyunres($1->loc, $1, NULL, 0);}
+        | name Toparen typelist Tcparen {$$ = mktyunres($1->loc, $1, $3.types, $3.ntypes);}
         ;
 
 functype: Toparen funcsig Tcparen {$$ = $2;}
@@ -462,17 +462,18 @@
         ;
 
 funcsig : argdefs Tret type
-            {$$ = mktyfunc($1.line, $1.nl, $1.nn, $3);}
+            {$$ = mktyfunc($2->loc, $1.nl, $1.nn, $3);}
         ;
 
 argdefs : typedeclcore {
-                $$.line = $1->line;
+                $$.loc = $1->loc;
                 $$.nl = NULL;
                 $$.nn = 0; lappend(&$$.nl, &$$.nn, $1);
             }
         | argdefs Tcomma declcore {lappend(&$$.nl, &$$.nn, $3);}
         | /* empty */ {
-                $$.line = line;
+                $$.loc.line = 0;
+                $$.loc.file = 0;
                 $$.nl = NULL;
                 $$.nn = 0;
             }
@@ -479,7 +480,7 @@
         ;
 
 tupledef: Toparen typelist Tcparen
-            {$$ = mktytuple($1->line, $2.types, $2.ntypes);}
+            {$$ = mktytuple($1->loc, $2.types, $2.ntypes);}
         ;
 
 typelist: type {
@@ -492,7 +493,7 @@
 
 structdef
         : Tstruct structbody Tendblk
-            {$$ = mktystruct($1->line, $2.nl, $2.nn);}
+            {$$ = mktystruct($1->loc, $2.nl, $2.nn);}
         ;
 
 structbody
@@ -516,7 +517,7 @@
 
 uniondef
         : Tunion unionbody Tendblk
-            {$$ = mktyunion($1->line, $2.ucl, $2.nucl);}
+            {$$ = mktyunion($1->loc, $2.ucl, $2.nucl);}
         ;
 
 unionbody
@@ -533,16 +534,16 @@
         ;
 
 unionelt /* nb: the ucon union type gets filled in when we have context */
-        : Ttick name type Tendln {$$ = mkucon($2->line, $2, NULL, $3);}
-        | Ttick name Tendln {$$ = mkucon($2->line, $2, NULL, NULL);}
+        : Ttick name type Tendln {$$ = mkucon($2->loc, $2, NULL, $3);}
+        | Ttick name Tendln {$$ = mkucon($2->loc, $2, NULL, NULL);}
         | Tendln {$$ = NULL;}
         ;
 
-goto    : Tgoto Tident {$$ = mkexpr($1->line, Ojmp, mklbl($2->line, $2->str), NULL);}
+goto    : Tgoto Tident {$$ = mkexpr($1->loc, Ojmp, mklbl($2->loc, $2->str), NULL);}
         ;
 
-retexpr : Tret expr {$$ = mkexpr($1->line, Oret, $2, NULL);}
-        | Tret {$$ = mkexpr($1->line, Oret, NULL);}
+retexpr : Tret expr {$$ = mkexpr($1->loc, Oret, $2, NULL);}
+        | Tret {$$ = mkexpr($1->loc, Oret, NULL);}
         | expr
         ;
 
@@ -561,7 +562,7 @@
         ;
 
 asnexpr : lorexpr asnop asnexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | lorexpr
         ;
 
@@ -579,17 +580,17 @@
         ;
 
 lorexpr : lorexpr Tlor landexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | landexpr
         ;
 
 landexpr: landexpr Tland cmpexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | cmpexpr
         ;
 
 cmpexpr : cmpexpr cmpop castexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | castexpr
         ;
 
@@ -597,7 +598,7 @@
 cmpop   : Teq | Tgt | Tlt | Tge | Tle | Tne ;
 
 castexpr: castexpr Tcast Toparen type Tcparen {
-                $$ = mkexpr($1->line, Ocast, $1, NULL);
+                $$ = mkexpr($1->loc, Ocast, $1, NULL);
                 $$->expr.type = $4;
             }
         | unionexpr
@@ -604,26 +605,26 @@
         ;
 
 unionexpr
-        : Ttick name unionexpr {$$ = mkexpr($1->line, Oucon, $2, $3, NULL);}
-        | Ttick name {$$ = mkexpr($1->line, Oucon, $2, NULL);}
+        : Ttick name unionexpr {$$ = mkexpr($1->loc, Oucon, $2, $3, NULL);}
+        | Ttick name {$$ = mkexpr($1->loc, Oucon, $2, NULL);}
         | borexpr
         ;
 
 
 borexpr : borexpr Tbor bandexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | borexpr Tbxor bandexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | bandexpr
         ;
 
 bandexpr: bandexpr Tband addexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | addexpr
         ;
 
 addexpr : addexpr addop mulexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | mulexpr
         ;
 
@@ -630,7 +631,7 @@
 addop   : Tplus | Tminus ;
 
 mulexpr : mulexpr mulop shiftexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | shiftexpr
         ;
 
@@ -639,7 +640,7 @@
 
 shiftexpr
         : shiftexpr shiftop prefixexpr
-            {$$ = mkexpr($1->line, binop($2->type), $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, binop($2->type), $1, $3, NULL);}
         | prefixexpr
         ;
 
@@ -646,12 +647,12 @@
 shiftop : Tbsl | Tbsr;
 
 prefixexpr
-        : Tinc prefixexpr      {$$ = mkexpr($1->line, Opreinc, $2, NULL);}
-        | Tdec prefixexpr      {$$ = mkexpr($1->line, Opredec, $2, NULL);}
-        | Tband prefixexpr     {$$ = mkexpr($1->line, Oaddr, $2, NULL);}
-        | Tlnot prefixexpr     {$$ = mkexpr($1->line, Olnot, $2, NULL);}
-        | Tbnot prefixexpr     {$$ = mkexpr($1->line, Obnot, $2, NULL);}
-        | Tminus prefixexpr    {$$ = mkexpr($1->line, Oneg, $2, NULL);}
+        : Tinc prefixexpr      {$$ = mkexpr($1->loc, Opreinc, $2, NULL);}
+        | Tdec prefixexpr      {$$ = mkexpr($1->loc, Opredec, $2, NULL);}
+        | Tband prefixexpr     {$$ = mkexpr($1->loc, Oaddr, $2, NULL);}
+        | Tlnot prefixexpr     {$$ = mkexpr($1->loc, Olnot, $2, NULL);}
+        | Tbnot prefixexpr     {$$ = mkexpr($1->loc, Obnot, $2, NULL);}
+        | Tminus prefixexpr    {$$ = mkexpr($1->loc, Oneg, $2, NULL);}
         | Tplus prefixexpr     {$$ = $2;} /* positive is a nop */
         | postfixexpr
         ;
@@ -658,19 +659,19 @@
 
 postfixexpr
         : postfixexpr Tdot Tident
-            {$$ = mkexpr($1->line, Omemb, $1, mkname($3->line, $3->str), NULL);}
+            {$$ = mkexpr($1->loc, Omemb, $1, mkname($3->loc, $3->str), NULL);}
         | postfixexpr Tinc
-            {$$ = mkexpr($1->line, Opostinc, $1, NULL);}
+            {$$ = mkexpr($1->loc, Opostinc, $1, NULL);}
         | postfixexpr Tdec
-            {$$ = mkexpr($1->line, Opostdec, $1, NULL);}
+            {$$ = mkexpr($1->loc, Opostdec, $1, NULL);}
         | postfixexpr Tosqbrac expr Tcsqbrac
-            {$$ = mkexpr($1->line, Oidx, $1, $3, NULL);}
+            {$$ = mkexpr($1->loc, Oidx, $1, $3, NULL);}
         | postfixexpr Tosqbrac optexpr Tcolon optexpr Tcsqbrac
-            {$$ = mksliceexpr($1->line, $1, $3, $5);}
+            {$$ = mksliceexpr($1->loc, $1, $3, $5);}
         | postfixexpr Tderef
-            {$$ = mkexpr($1->line, Oderef, $1, NULL);}
+            {$$ = mkexpr($1->loc, Oderef, $1, NULL);}
         | postfixexpr Toparen arglist Tcparen
-            {$$ = mkcall($1->line, $1, $3.nl, $3.nn);}
+            {$$ = mkcall($1->loc, $1, $3.nl, $3.nn);}
         | atomicexpr
         ;
 
@@ -684,12 +685,12 @@
 
 atomicexpr
         : Tident
-            {$$ = mkexpr($1->line, Ovar, mkname($1->line, $1->str), NULL);}
+            {$$ = mkexpr($1->loc, Ovar, mkname($1->loc, $1->str), NULL);}
         | literal
         | Toparen expr Tcparen
             {$$ = $2;}
         | Tsizeof Toparen type Tcparen
-            {$$ = mkexpr($1->line, Osize, mkpseudodecl($3), NULL);}
+            {$$ = mkexpr($1->loc, Osize, mkpseudodecl($3), NULL);}
         ;
 
 tupbody : tuphead tuprest
@@ -709,30 +710,30 @@
         | tuprest Tcomma expr {lappend(&$$.nl, &$$.nn, $3);}
         ;
 
-literal : funclit       {$$ = mkexpr($1->line, Olit, $1, NULL);}
-        | littok        {$$ = mkexpr($1->line, Olit, $1, NULL);}
+literal : funclit       {$$ = mkexpr($1->loc, Olit, $1, NULL);}
+        | littok        {$$ = mkexpr($1->loc, Olit, $1, NULL);}
         | seqlit        {$$ = $1;}
         | tuplit        {$$ = $1;}
         ;
 
 tuplit  : Toparen tupbody Tcparen
-            {$$ = mkexprl($1->line, Otup, $2.nl, $2.nn);}
+            {$$ = mkexprl($1->loc, Otup, $2.nl, $2.nn);}
 
-littok  : Tstrlit       {$$ = mkstr($1->line, $1->str);}
-        | Tchrlit       {$$ = mkchar($1->line, $1->chrval);}
-        | Tfloatlit     {$$ = mkfloat($1->line, $1->fltval);}
-        | Tboollit      {$$ = mkbool($1->line, !strcmp($1->str, "true"));}
+littok  : Tstrlit       {$$ = mkstr($1->loc, $1->str);}
+        | Tchrlit       {$$ = mkchar($1->loc, $1->chrval);}
+        | Tfloatlit     {$$ = mkfloat($1->loc, $1->fltval);}
+        | Tboollit      {$$ = mkbool($1->loc, !strcmp($1->str, "true"));}
         | Tintlit {
-                $$ = mkint($1->line, $1->intval);
+                $$ = mkint($1->loc, $1->intval);
                 if ($1->inttype)
-                    $$->lit.type = mktype($1->line, $1->inttype);
+                    $$->lit.type = mktype($1->loc, $1->inttype);
             }
         ;
 
 funclit : Tobrace params Tendln blkbody Tcbrace
-            {$$ = mkfunc($1->line, $2.nl, $2.nn, mktyvar($3->line), $4);}
+            {$$ = mkfunc($1->loc, $2.nl, $2.nn, mktyvar($3->loc), $4);}
         | Tobrace params Tret type Tendln blkbody Tcbrace
-            {$$ = mkfunc($1->line, $2.nl, $2.nn, $4, $6);}
+            {$$ = mkfunc($1->loc, $2.nl, $2.nn, $4, $6);}
         ;
 
 params  : declcore {
@@ -745,11 +746,11 @@
         ;
 
 seqlit  : Tosqbrac arrayelts Tcsqbrac
-            {$$ = mkexprl($1->line, Oarr, $2.nl, $2.nn);}
+            {$$ = mkexprl($1->loc, Oarr, $2.nl, $2.nn);}
         | Tosqbrac structelts Tcsqbrac
-            {$$ = mkexprl($1->line, Ostruct, $2.nl, $2.nn);}
+            {$$ = mkexprl($1->loc, Ostruct, $2.nl, $2.nn);}
         | Tosqbrac Tcsqbrac /* [] is the empty array. */
-            {$$ = mkexprl($1->line, Oarr, NULL, 0);}
+            {$$ = mkexprl($1->loc, Oarr, NULL, 0);}
         ;
 
 arrayelts
@@ -756,10 +757,10 @@
         : optendlns arrayelt {
                 $$.nl = NULL;
                 $$.nn = 0;
-                lappend(&$$.nl, &$$.nn, mkidxinit($2->line, mkint($2->line, 0), $2));
+                lappend(&$$.nl, &$$.nn, mkidxinit($2->loc, mkint($2->loc, 0), $2));
             }
         | arrayelts Tcomma optendlns arrayelt
-             {lappend(&$$.nl, &$$.nn, mkidxinit($4->line, mkint($4->line, $$.nn), $4));}
+             {lappend(&$$.nl, &$$.nn, mkidxinit($4->loc, mkint($4->loc, $$.nn), $4));}
         | arrayelts Tcomma optendlns
         ;
 
@@ -777,7 +778,7 @@
         ;
 
 structelt: optendlns Tdot Tident Tasn expr optendlns
-            {$$ = mkidxinit($2->line, mkname($3->line, $3->str), $5);}
+            {$$ = mkidxinit($2->loc, mkname($3->loc, $3->str), $5);}
          ;
 
 optendlns  : /* none */
@@ -797,34 +798,34 @@
         ;
 
 break   : Tbreak
-            {$$ = mkexpr($1->line, Obreak, NULL);}
+            {$$ = mkexpr($1->loc, Obreak, NULL);}
         ;
 
 continue   : Tcontinue
-            {$$ = mkexpr($1->line, Ocontinue, NULL);}
+            {$$ = mkexpr($1->loc, Ocontinue, NULL);}
         ;
 
 forstmt : Tfor optexprln optexprln optexprln block
-            {$$ = mkloopstmt($1->line, $2, $3, $4, $5);}
+            {$$ = mkloopstmt($1->loc, $2, $3, $4, $5);}
         | Tfor expr Tin exprln block
-            {$$ = mkiterstmt($1->line, $2, $4, $5);}
+            {$$ = mkiterstmt($1->loc, $2, $4, $5);}
         /* FIXME: allow decls in for loops
         | Tfor decl Tendln optexprln optexprln block
-            {$$ = mkloopstmt($1->line, $2, $4, $5, $6);}
+            {$$ = mkloopstmt($1->loc, $2, $4, $5, $6);}
         */
         ;
 
 whilestmt
         : Twhile exprln block
-            {$$ = mkloopstmt($1->line, NULL, $2, NULL, $3);}
+            {$$ = mkloopstmt($1->loc, NULL, $2, NULL, $3);}
         ;
 
 ifstmt  : Tif exprln blkbody elifs
-            {$$ = mkifstmt($1->line, $2, $3, $4);}
+            {$$ = mkifstmt($1->loc, $2, $3, $4);}
         ;
 
 elifs   : Telif exprln blkbody elifs
-            {$$ = mkifstmt($1->line, $2, $3, $4);}
+            {$$ = mkifstmt($1->loc, $2, $3, $4);}
         | Telse block
             {$$ = $2;}
         | Tendblk
@@ -832,7 +833,7 @@
         ;
 
 matchstmt: Tmatch exprln optendlns Tbor matches Tendblk
-            {$$ = mkmatchstmt($1->line, $2, $5.nl, $5.nn);}
+            {$$ = mkmatchstmt($1->loc, $2, $5.nl, $5.nn);}
          ;
 
 matches : match {
@@ -847,7 +848,7 @@
             }
         ;
 
-match   : expr Tcolon blkbody Tendln {$$ = mkmatch($1->line, $1, $3);}
+match   : expr Tcolon blkbody Tendln {$$ = mkmatch($1->loc, $1, $3);}
         ;
 
 block   : blkbody Tendblk
@@ -855,7 +856,7 @@
 
 blkbody : decl {
                 size_t i;
-                $$ = mkblock(line, mkstab());
+                $$ = mkblock($1.loc, mkstab());
                 for (i = 0; i < $1.nn; i++) {
                     putdcl($$->block.scope, $1.nl[i]);
                     lappend(&$$->block.stmts, &$$->block.nstmts, $1.nl[i]);
@@ -862,7 +863,7 @@
                 }
             }
         | stmt {
-                $$ = mkblock(line, mkstab());
+                $$ = mkblock(curloc, mkstab());
                 if ($1)
                     lappend(&$$->block.stmts, &$$->block.nstmts, $1);
             }
@@ -881,7 +882,7 @@
         ;
 
 label   : Tcolon Tident
-            {$$ = mklbl($2->line, $2->str);}
+            {$$ = mklbl($2->loc, $2->str);}
         ;
 
 %%
@@ -896,7 +897,7 @@
             return;
         }
     }
-    lfatal(t->line, t->file, "Constraint %s does not exist", str);
+    lfatal(t->loc, "Constraint %s does not exist", str);
 }
 
 static Node *mkpseudodecl(Type *t)
@@ -903,9 +904,12 @@
 {
     static int nextpseudoid;
     char buf[128];
+    Srcloc l;
 
+    l.line = -1;
+    l.file = 0;
     snprintf(buf, 128, ".pdecl%d", nextpseudoid++);
-    return mkdecl(-1, mkname(-1, buf), t);
+    return mkdecl(l, mkname(l, buf), t);
 }
 
 static void setattrs(Node *dcl, char **attrs, size_t nattrs)
@@ -947,14 +951,6 @@
     }
 }
 
-void yyerror(const char *s)
-{
-    fprintf(stderr, "%s:%d: %s", filename, line, s);
-    if (curtok->str)
-        fprintf(stderr, " near \"%s\"", curtok->str);
-    fprintf(stderr, "\n");
-    exit(1);
-}
 
 static Op binop(int tt)
 {
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -301,7 +301,7 @@
     else
         t->traits = bsdup(base->traits);
     if (tyinfinite(st, t, NULL))
-        lfatal(t->line, t->file, "Type %s includes itself", tystr(t));
+        lfatal(t->loc, "Type %s includes itself", tystr(t));
     st->ingeneric--;
 }
 
@@ -379,12 +379,12 @@
     if (n->lit.type)
         return n->lit.type;
     switch (n->lit.littype) {
-        case Lchr:      return mktype(n->line, Tychar);                         break;
-        case Lbool:     return mktype(n->line, Tybool);                         break;
-        case Lint:      return mktylike(n->line, Tyint);                        break;
-        case Lflt:      return mktylike(n->line, Tyflt64);                    break;
-        case Lstr:      return mktyslice(n->line, mktype(n->line, Tybyte));     break;
-        case Llbl:      return mktyptr(n->line, mktype(n->line, Tyvoid));       break;
+        case Lchr:      return mktype(n->loc, Tychar);                         break;
+        case Lbool:     return mktype(n->loc, Tybool);                         break;
+        case Lint:      return mktylike(n->loc, Tyint);                        break;
+        case Lflt:      return mktylike(n->loc, Tyflt64);                    break;
+        case Lstr:      return mktyslice(n->loc, mktype(n->loc, Tybyte));     break;
+        case Llbl:      return mktyptr(n->loc, mktype(n->loc, Tyvoid));       break;
         case Lfunc:     return n->lit.fnval->func.type;                         break;
     };
     die("Bad lit type %d", n->lit.littype);
@@ -398,7 +398,7 @@
 
     if (fallback->type != Tyunion)
         return fallback;
-    t = mktylike(fallback->line, fallback->type);
+    t = mktylike(fallback->loc, fallback->type);
     htput(st->delayed, t, fallback);
     if (debugopt['u']) {
         from = tystr(t);
@@ -789,7 +789,7 @@
 
     if (ft->type == Tyvar) {
         /* the first arg is the function itself, so it shouldn't be counted */
-        ft = mktyfunc(n->line, &n->expr.args[1], n->expr.nargs - 1, mktyvar(n->line));
+        ft = mktyfunc(n->loc, &n->expr.args[1], n->expr.nargs - 1, mktyvar(n->loc));
         unify(st, n, ft, type(st, n->expr.args[0]));
     }
     for (i = 1; i < n->expr.nargs; i++) {
@@ -907,7 +907,7 @@
             if (!tg)
                 puttype(globls, nx, tx);
             else
-                fatal(nx, "Exported type %s already declared on line %d", namestr(nx), tg->line);
+                fatal(nx, "Exported type %s already declared on line %d", namestr(nx), tg->loc);
         } else {
             tg = gettype(globls, nx);
             if (tg)
@@ -928,7 +928,7 @@
             if (!trg)
                 puttrait(globls, nx, trx);
             else
-                fatal(nx, "Exported trait %s already declared on line %d", namestr(nx), trg->name->line);
+                fatal(nx, "Exported trait %s already declared on line %d", namestr(nx), trg->name->loc);
         } else {
             trg = gettrait(globls, nx);
             if (trg && !trg->isproto) {
@@ -966,7 +966,7 @@
                 putimpl(globls, nx);
             } else {
                 fatal(nx, "Double trait impl body for %s %s on line %d\n",
-                      namestr(nx->impl.traitname), tystr(nx->impl.type), ng->line);
+                      namestr(nx->impl.traitname), tystr(nx->impl.type), ng->loc);
             }
         }
         lappend(&exportimpls, &nexportimpls, ng);
@@ -982,9 +982,9 @@
          * body */
         if (ng) {
             if (nx->decl.init)
-                fatal(nx, "Export %s double-defined on line %d", ctxstr(st, nx), ng->line);
+                fatal(nx, "Export %s double-defined on line %d", ctxstr(st, nx), ng->loc);
             if (nx->decl.isgeneric != ng->decl.isgeneric)
-                fatal(nx, "Export %s defined with different genericness on line %d", ctxstr(st, nx), ng->line);
+                fatal(nx, "Export %s defined with different genericness on line %d", ctxstr(st, nx), ng->loc);
             mergeattrs(ng, nx);
             mergeattrs(nx, ng);
             unify(st, nx, type(st, ng), type(st, nx));
@@ -1007,7 +1007,7 @@
         /* if an export has an initializer, it shouldn't be declared in the
          * body */
         if (ux && ug)
-            lfatal(ux->line, ux->file, "Union constructor double defined on %d", ux->line);
+            lfatal(ux->loc, "Union constructor double defined on %d", ux->loc);
         else if (!ug)
           putucon(globls, ux);
         else
@@ -1033,7 +1033,7 @@
     if (s->decl.isgeneric && !st->ingeneric) {
         addspecialization(st, n, curstab());
         if (t->type == Tyvar) {
-            settype(st, n, mktyvar(n->line));
+            settype(st, n, mktyvar(n->loc));
             delayedcheck(st, n, curstab());
         } else {
             settype(st, n, t);
@@ -1070,11 +1070,11 @@
         return;
 
     /* substitute the namespaced name */
-    nsname = mknsname(n->line, namestr(name), namestr(args[1]));
+    nsname = mknsname(n->loc, namestr(name), namestr(args[1]));
     s = getdcl(stab, args[1]);
     if (!s)
         fatal(n, "Undeclared var %s.%s", nsname->name.ns, nsname->name.name);
-    var = mkexpr(n->line, Ovar, nsname, NULL);
+    var = mkexpr(n->loc, Ovar, nsname, NULL);
     initvar(st, var, s);
     *ret = var;
 }
@@ -1089,7 +1089,7 @@
         if (!n->expr.args[i]->expr.isconst)
             *isconst = 0;
     }
-    settype(st, n, mktyvar(n->line));
+    settype(st, n, mktyvar(n->loc));
     delayedcheck(st, n, curstab());
 }
 
@@ -1100,8 +1100,8 @@
     Node *len;
 
     *isconst = 1;
-    len = mkintlit(n->line, n->expr.nargs);
-    t = mktyarray(n->line, mktyvar(n->line), len);
+    len = mkintlit(n->loc, n->expr.nargs);
+    t = mktyarray(n->loc, mktyvar(n->loc), len);
     for (i = 0; i < n->expr.nargs; i++) {
         infernode(st, n->expr.args[i], NULL, NULL);
         unify(st, n, t->sub[0], type(st, n->expr.args[i]));
@@ -1124,7 +1124,7 @@
         types[i] = type(st, n->expr.args[i]);
     }
     *isconst = n->expr.isconst;
-    settype(st, n, mktytuple(n->line, types, n->expr.nargs));
+    settype(st, n, mktytuple(n->loc, types, n->expr.nargs));
 }
 
 static void inferucon(Inferstate *st, Node *n, int *isconst)
@@ -1189,8 +1189,8 @@
                 else
                     fatal(n, "Can't match against non-constant variables near %s", ctxstr(st, n));
             } else {
-                t = mktyvar(n->line);
-                s = mkdecl(n->line, n->expr.args[0], t);
+                t = mktyvar(n->loc);
+                s = mkdecl(n->loc, n->expr.args[0], t);
                 s->decl.init = val;
                 settype(st, n, t);
                 lappend(bind, nbind, s);
@@ -1332,22 +1332,22 @@
             t = type(st, args[0]);
             for (i = 1; i < nargs; i++)
                 unify(st, n, t, type(st, args[i]));
-            settype(st, n, mktype(-1, Tybool));
+            settype(st, n, mktype(Zloc, Tybool));
             break;
 
         /* reach into a type and pull out subtypes */
         case Oaddr:     /* &@a -> @a* */
             infersub(st, n, ret, sawret, &isconst);
-            settype(st, n, mktyptr(n->line, type(st, args[0])));
+            settype(st, n, mktyptr(n->loc, type(st, args[0])));
             break;
         case Oderef:    /* *@a* ->  @a */
             infersub(st, n, ret, sawret, &isconst);
-            t = unify(st, n, type(st, args[0]), mktyptr(n->line, mktyvar(n->line)));
+            t = unify(st, n, type(st, args[0]), mktyptr(n->loc, mktyvar(n->loc)));
             settype(st, n, t->sub[0]);
             break;
         case Oidx:      /* @a[@b::tcint] -> @a */
             infersub(st, n, ret, sawret, &isconst);
-            t = mktyidxhack(n->line, mktyvar(n->line));
+            t = mktyidxhack(n->loc, mktyvar(n->loc));
             unify(st, n, type(st, args[0]), t);
             constrain(st, n, type(st, args[1]), traittab[Tcint]);
             settype(st, n, t->sub[0]);
@@ -1354,22 +1354,22 @@
             break;
         case Oslice:    /* @a[@b::tcint,@b::tcint] -> @a[,] */
             infersub(st, n, ret, sawret, &isconst);
-            t = mktyidxhack(n->line, mktyvar(n->line));
+            t = mktyidxhack(n->loc, mktyvar(n->loc));
             unify(st, n, type(st, args[0]), t);
             constrain(st, n, type(st, args[1]), traittab[Tcint]);
             constrain(st, n, type(st, args[2]), traittab[Tcint]);
-            settype(st, n, mktyslice(n->line, t->sub[0]));
+            settype(st, n, mktyslice(n->loc, t->sub[0]));
             break;
 
         /* special cases */
         case Omemb:     /* @a.Ident -> @b, verify type(@a.Ident)==@b later */
             infersub(st, n, ret, sawret, &isconst);
-            settype(st, n, mktyvar(n->line));
+            settype(st, n, mktyvar(n->loc));
             delayedcheck(st, n, curstab());
             break;
         case Osize:     /* sizeof @a -> size */
             infersub(st, n, ret, sawret, &isconst);
-            settype(st, n, mktylike(n->line, Tyuint));
+            settype(st, n, mktylike(n->loc, Tyuint));
             break;
         case Ocall:     /* (@a, @b, @c, ... -> @r)(@a,@b,@c, ... -> @r) -> @r */
             infersub(st, n, ret, sawret, &isconst);
@@ -1388,17 +1388,17 @@
             if (nargs)
                 t = unify(st, n, ret, type(st, args[0]));
             else
-                t =  unify(st, n, mktype(-1, Tyvoid), ret);
+                t =  unify(st, n, mktype(Zloc, Tyvoid), ret);
             settype(st, n, t);
             break;
         case Obreak:
         case Ocontinue:
             /* nullary: nothing to infer. */
-            settype(st, n, mktype(-1, Tyvoid));
+            settype(st, n, mktype(Zloc, Tyvoid));
             break;
         case Ojmp:     /* goto void* -> void */
             infersub(st, n, ret, sawret, &isconst);
-            settype(st, n, mktype(-1, Tyvoid));
+            settype(st, n, mktype(Zloc, Tyvoid));
             break;
         case Ovar:      /* a:@a -> @a */
             infersub(st, n, ret, sawret, &isconst);
@@ -1439,7 +1439,7 @@
             break;
         case Olbl:      /* :lbl -> void* */
             infersub(st, n, ret, sawret, &isconst);
-            settype(st, n, mktyptr(n->line, mktype(-1, Tyvoid)));
+            settype(st, n, mktyptr(n->loc, mktype(Zloc, Tyvoid)));
         case Obad: case Ocjmp: case Oset:
         case Oslbase: case Osllen:
         case Oblit: case Numops:
@@ -1465,7 +1465,7 @@
     infernode(st, n->func.body, n->func.type->sub[0], &sawret);
     /* if there's no return stmt in the function, assume void ret */
     if (!sawret)
-        unify(st, n, type(st, n)->sub[0], mktype(-1, Tyvoid));
+        unify(st, n, type(st, n)->sub[0], mktype(Zloc, Tyvoid));
 }
 
 static void specializeimpl(Inferstate *st, Node *n)
@@ -1522,7 +1522,7 @@
         putdcl(file->file.globls, dcl);
         if (debugopt['S'])
             printf("specializing trait [%d]%s:%s => %s:%s\n",
-                   n->line, namestr(proto->decl.name), tystr(type(st, proto)), namestr(name), tystr(ty));
+                   n->loc.line, namestr(proto->decl.name), tystr(type(st, proto)), namestr(name), tystr(ty));
         lappend(&file->file.stmts, &file->file.nstmts, dcl);
     }
 }
@@ -1620,7 +1620,7 @@
             infernode(st, n->ifstmt.cond, NULL, sawret);
             infernode(st, n->ifstmt.iftrue, ret, sawret);
             infernode(st, n->ifstmt.iffalse, ret, sawret);
-            unify(st, n, type(st, n->ifstmt.cond), mktype(n->line, Tybool));
+            unify(st, n, type(st, n->ifstmt.cond), mktype(n->loc, Tybool));
             break;
         case Nloopstmt:
             infernode(st, n->loopstmt.init, ret, sawret);
@@ -1627,7 +1627,7 @@
             infernode(st, n->loopstmt.cond, NULL, sawret);
             infernode(st, n->loopstmt.step, ret, sawret);
             infernode(st, n->loopstmt.body, ret, sawret);
-            unify(st, n, type(st, n->loopstmt.cond), mktype(n->line, Tybool));
+            unify(st, n, type(st, n->loopstmt.cond), mktype(n->loc, Tybool));
             break;
         case Niterstmt:
             bound = NULL;
@@ -1640,7 +1640,7 @@
             infernode(st, n->iterstmt.seq, NULL, sawret);
             infernode(st, n->iterstmt.body, ret, sawret);
 
-            t = mktyidxhack(n->line, mktyvar(n->line));
+            t = mktyidxhack(n->loc, mktyvar(n->loc));
             constrain(st, n, type(st, n->iterstmt.seq), traittab[Tcidx]);
             unify(st, n, type(st, n->iterstmt.seq), t);
             unify(st, n, type(st, n->iterstmt.elt), t->sub[0]);
@@ -1698,9 +1698,9 @@
     char buf[1024];
 
     if (!tyint)
-        tyint = mktype(-1, Tyint);
+        tyint = mktype(Zloc, Tyint);
     if (!tyflt)
-        tyflt = mktype(-1, Tyflt64);
+        tyflt = mktype(Zloc, Tyflt64);
 
     t = tysearch(orig);
     if (orig->type == Tyvar && hthas(st->delayed, orig)) {
@@ -1737,7 +1737,7 @@
     if (t->type == Tyvar) {
         if (debugopt['T'])
             dump(file, stdout);
-        lfatal(t->line, t->file, "underconstrained type %s near %s", tyfmt(buf, 1024, t), ctxstr(st, ctx));
+        lfatal(t->loc, "underconstrained type %s near %s", tyfmt(buf, 1024, t), ctxstr(st, ctx));
     }
     if (debugopt['u'] && !tyeq(orig, t)) {
         from = tystr(orig);
--- a/parse/node.c
+++ b/parse/node.c
@@ -18,7 +18,7 @@
 Node **exportimpls;
 size_t nexportimpls;
 
-Node *mknode(int line, Ntype nt)
+Node *mknode(Srcloc loc, Ntype nt)
 {
     Node *n;
 
@@ -25,7 +25,7 @@
     n = zalloc(sizeof(Node));
     n->nid = maxnid++;
     n->type = nt;
-    n->line = line;
+    n->loc = loc;
     return n;
 }
 
@@ -33,16 +33,16 @@
 {
     Node *n;
 
-    n = mknode(-1, Nfile);
+    n = mknode(Zloc, Nfile);
     lappend(&n->file.files, &n->file.nfiles, strdup(name));
     return n;
 }
 
-Node *mkuse(int line, char *use, int islocal)
+Node *mkuse(Srcloc loc, char *use, int islocal)
 {
     Node *n;
 
-    n = mknode(line, Nuse);
+    n = mknode(loc, Nuse);
     n->use.name = strdup(use);
     n->use.islocal = islocal;
 
@@ -49,20 +49,20 @@
     return n;
 }
 
-Node *mksliceexpr(int line, Node *sl, Node *base, Node *off)
+Node *mksliceexpr(Srcloc loc, Node *sl, Node *base, Node *off)
 {
     if (!base)
-        base = mkintlit(line, 0);
+        base = mkintlit(loc, 0);
     if (!off)
-        off = mkexpr(line, Omemb, sl, mkname(line, "len"), NULL);
-    return mkexpr(line, Oslice, sl, base, off, NULL);
+        off = mkexpr(loc, Omemb, sl, mkname(loc, "len"), NULL);
+    return mkexpr(loc, Oslice, sl, base, off, NULL);
 }
 
-Node *mkexprl(int line, Op op, Node **args, size_t nargs)
+Node *mkexprl(Srcloc loc, Op op, Node **args, size_t nargs)
 {
     Node *n;
 
-    n = mknode(line, Nexpr);
+    n = mknode(loc, Nexpr);
     n->expr.op = op;
     n->expr.args = args;
     n->expr.nargs = nargs;
@@ -69,13 +69,13 @@
     return n;
 }
 
-Node *mkexpr(int line, Op op, ...)
+Node *mkexpr(Srcloc loc, Op op, ...)
 {
     Node *n;
     va_list ap;
     Node *arg;
 
-    n = mknode(line, Nexpr);
+    n = mknode(loc, Nexpr);
     n->expr.op = op;
     va_start(ap, op);
     while ((arg = va_arg(ap, Node*)) != NULL)
@@ -85,22 +85,22 @@
     return n;
 }
 
-Node *mkcall(int line, Node *fn, Node **args, size_t nargs) 
+Node *mkcall(Srcloc loc, Node *fn, Node **args, size_t nargs) 
 {
     Node *n;
     size_t i;
 
-    n = mkexpr(line, Ocall, fn, NULL);
+    n = mkexpr(loc, Ocall, fn, NULL);
     for (i = 0; i < nargs; i++)
         lappend(&n->expr.args, &n->expr.nargs, args[i]);
     return n;
 }
 
-Node *mkifstmt(int line, Node *cond, Node *iftrue, Node *iffalse)
+Node *mkifstmt(Srcloc loc, Node *cond, Node *iftrue, Node *iffalse)
 {
     Node *n;
 
-    n = mknode(line, Nifstmt);
+    n = mknode(loc, Nifstmt);
     n->ifstmt.cond = cond;
     n->ifstmt.iftrue = iftrue;
     n->ifstmt.iffalse = iffalse;
@@ -108,11 +108,11 @@
     return n;
 }
 
-Node *mkloopstmt(int line, Node *init, Node *cond, Node *incr, Node *body)
+Node *mkloopstmt(Srcloc loc, Node *init, Node *cond, Node *incr, Node *body)
 {
     Node *n;
 
-    n = mknode(line, Nloopstmt);
+    n = mknode(loc, Nloopstmt);
     n->loopstmt.init = init;
     n->loopstmt.cond = cond;
     n->loopstmt.step = incr;
@@ -121,11 +121,11 @@
     return n;
 }
 
-Node *mkiterstmt(int line, Node *elt, Node *seq, Node *body)
+Node *mkiterstmt(Srcloc loc, Node *elt, Node *seq, Node *body)
 {
     Node *n;
 
-    n = mknode(line, Niterstmt);
+    n = mknode(loc, Niterstmt);
     n->iterstmt.elt = elt;
     n->iterstmt.seq = seq;
     n->iterstmt.body = body;
@@ -133,11 +133,11 @@
     return n;
 }
 
-Node *mkmatchstmt(int line, Node *val, Node **matches, size_t nmatches)
+Node *mkmatchstmt(Srcloc loc, Node *val, Node **matches, size_t nmatches)
 {
     Node *n;
 
-    n = mknode(line, Nmatchstmt);
+    n = mknode(loc, Nmatchstmt);
     n->matchstmt.val = val;
     n->matchstmt.matches = matches;
     n->matchstmt.nmatches = nmatches;
@@ -144,52 +144,52 @@
     return n;
 }
 
-Node *mkmatch(int line, Node *pat, Node *body)
+Node *mkmatch(Srcloc loc, Node *pat, Node *body)
 {
     Node *n;
 
-    n = mknode(line, Nmatch);
+    n = mknode(loc, Nmatch);
     n->match.pat = pat;
     n->match.block = body;
     return n;
 }
 
-Node *mkfunc(int line, Node **args, size_t nargs, Type *ret, Node *body)
+Node *mkfunc(Srcloc loc, Node **args, size_t nargs, Type *ret, Node *body)
 {
     Node *n;
     Node *f;
     size_t i;
 
-    f = mknode(line, Nfunc);
+    f = mknode(loc, Nfunc);
     f->func.args = args;
     f->func.nargs = nargs;
     f->func.body = body;
     f->func.scope = mkstab();
-    f->func.type = mktyfunc(line, args, nargs, ret);
+    f->func.type = mktyfunc(loc, args, nargs, ret);
 
     for (i = 0; i < nargs; i++)
         putdcl(f->func.scope, args[i]);
 
-    n = mknode(line, Nlit);
+    n = mknode(loc, Nlit);
     n->lit.littype = Lfunc;
     n->lit.fnval = f;
     return n;
 }
 
-Node *mkblock(int line, Stab *scope)
+Node *mkblock(Srcloc loc, Stab *scope)
 {
     Node *n;
 
-    n = mknode(line, Nblock);
+    n = mknode(loc, Nblock);
     n->block.scope = scope;
     return n;
 }
 
-Node *mkimplstmt(int line, Node *name, Type *t, Node **decls, size_t ndecls)
+Node *mkimplstmt(Srcloc loc, Node *name, Type *t, Node **decls, size_t ndecls)
 {
     Node *n;
 
-    n = mknode(line, Nimpl);
+    n = mknode(loc, Nimpl);
     n->impl.traitname = name;
     n->impl.type = t;
     n->impl.decls = decls;
@@ -198,27 +198,27 @@
 }
 
 
-Node *mkintlit(int line, uvlong val)
+Node *mkintlit(Srcloc loc, uvlong val)
 {
-    return mkexpr(line, Olit, mkint(line, val), NULL);
+    return mkexpr(loc, Olit, mkint(loc, val), NULL);
 }
 
-Node *mklbl(int line, char *lbl)
+Node *mklbl(Srcloc loc, char *lbl)
 {
     Node *n;
 
     assert(lbl != NULL);
-    n = mknode(line, Nlit);
+    n = mknode(loc, Nlit);
     n->lit.littype = Llbl;
     n->lit.lblval = strdup(lbl);
-    return mkexpr(line, Olit, n, NULL);
+    return mkexpr(loc, Olit, n, NULL);
 }
 
-Node *mkstr(int line, char *val)
+Node *mkstr(Srcloc loc, char *val)
 {
     Node *n;
 
-    n = mknode(line, Nlit);
+    n = mknode(loc, Nlit);
     n->lit.littype = Lstr;
     n->lit.strval = strdup(val);
 
@@ -225,11 +225,11 @@
     return n;
 }
 
-Node *mkint(int line, uint64_t val)
+Node *mkint(Srcloc loc, uint64_t val)
 {
     Node *n;
 
-    n = mknode(line, Nlit);
+    n = mknode(loc, Nlit);
     n->lit.littype = Lint;
     n->lit.intval = val;
 
@@ -236,11 +236,11 @@
     return n;
 }
 
-Node *mkchar(int line, uint32_t val)
+Node *mkchar(Srcloc loc, uint32_t val)
 {
     Node *n;
 
-    n = mknode(line, Nlit);
+    n = mknode(loc, Nlit);
     n->lit.littype = Lchr;
     n->lit.chrval = val;
 
@@ -247,11 +247,11 @@
     return n;
 }
 
-Node *mkfloat(int line, double val)
+Node *mkfloat(Srcloc loc, double val)
 {
     Node *n;
 
-    n = mknode(line, Nlit);
+    n = mknode(loc, Nlit);
     n->lit.littype = Lflt;
     n->lit.fltval = val;
 
@@ -258,27 +258,27 @@
     return n;
 }
 
-Node *mkidxinit(int line, Node *idx, Node *init)
+Node *mkidxinit(Srcloc loc, Node *idx, Node *init)
 {
     init->expr.idx = idx;
     return init;
 }
 
-Node *mkname(int line, char *name)
+Node *mkname(Srcloc loc, char *name)
 {
     Node *n;
 
-    n = mknode(line, Nname);
+    n = mknode(loc, Nname);
     n->name.name = strdup(name);
 
     return n;
 }
 
-Node *mknsname(int line, char *ns, char *name)
+Node *mknsname(Srcloc loc, char *ns, char *name)
 {
     Node *n;
 
-    n = mknode(line, Nname);
+    n = mknode(loc, Nname);
     n->name.ns = strdup(ns);
     n->name.name = strdup(name);
 
@@ -285,11 +285,11 @@
     return n;
 }
 
-Node *mkdecl(int line, Node *name, Type *ty)
+Node *mkdecl(Srcloc loc, Node *name, Type *ty)
 {
     Node *n;
 
-    n = mknode(line, Ndecl);
+    n = mknode(loc, Ndecl);
     n->decl.did = ndecls;
     n->decl.name = name;
     n->decl.type = ty;
@@ -297,12 +297,12 @@
     return n;
 }
 
-Ucon *mkucon(int line, Node *name, Type *ut, Type *et)
+Ucon *mkucon(Srcloc loc, Node *name, Type *ut, Type *et)
 {
     Ucon *uc;
 
     uc = zalloc(sizeof(Ucon));
-    uc->line = line;
+    uc->loc = loc;
     uc->name = name;
     uc->utype = ut;
     uc->etype = et;
@@ -309,11 +309,11 @@
     return uc;
 }
 
-Node *mkbool(int line, int val)
+Node *mkbool(Srcloc loc, int val)
 {
     Node *n;
 
-    n = mknode(line, Nlit);
+    n = mknode(loc, Nlit);
     n->lit.littype = Lbool;
     n->lit.boolval = val;
 
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -10,6 +10,8 @@
 typedef long long       vlong;
 typedef unsigned long long uvlong;
 
+typedef struct Srcloc Srcloc;
+
 typedef struct Bitset Bitset;
 typedef struct Htab Htab;
 typedef struct Optctx Optctx;
@@ -55,6 +57,12 @@
     Ntraits
 } Tc;
 
+#define Zloc ((Srcloc){.line=-1, .file=0})
+struct Srcloc {
+    int line;
+    int file;
+};
+
 typedef enum {
     Visintern,
     Visexport,
@@ -85,7 +93,7 @@
 
 struct Tok {
     int type;
-    int line;
+    Srcloc loc;
     char *str;
 
     /* values parsed out */
@@ -113,8 +121,7 @@
 struct Type {
     Ty type;
     int tid;
-    int line;
-    int file;
+    Srcloc loc;
     Vis vis;
 
     int resolved;       /* Have we resolved the subtypes? Prevents infinite recursion. */
@@ -147,8 +154,7 @@
 };
 
 struct Ucon {
-    int line;   /* line declared on */
-    int file;   /* file index */
+    Srcloc loc;
     size_t id;  /* unique id */
     int synth;  /* is it generated? */
     Node *name; /* ucon name */
@@ -170,8 +176,7 @@
 };
 
 struct Node {
-    int line;
-    int fid;
+    Srcloc loc;
     Ntype type;
     int nid;
     union {
@@ -338,9 +343,9 @@
 };
 
 /* globals */
+extern Srcloc curloc;
 extern char *filename;
 extern Tok *curtok;     /* the last token we tokenized */
-extern int line;        /* the last line number we tokenized */
 extern Node *file;      /* the current file we're compiling */
 extern Type **tytab;    /* type -> type map used by inference. size maintained by type creation code */
 extern Type **types;
@@ -406,8 +411,8 @@
 void *xrealloc(void *p, size_t size);
 void die(char *msg, ...) FATAL;
 void fatal(Node *n, char *fmt, ...) FATAL;
-void lfatal(int line, int file, char *fmt, ...) FATAL;
-void lfatalv(int line, int file, char *fmt, va_list ap) FATAL;
+void lfatal(Srcloc l, char *fmt, ...) FATAL;
+void lfatalv(Srcloc l, char *fmt, va_list ap) FATAL;
 char *strdupn(char *s, size_t len);
 char *strjoin(char *u, char *v);
 void *memdup(void *mem, size_t len);
@@ -445,22 +450,22 @@
 /* type creation */
 void tyinit(Stab *st); /* sets up built in types */
 
-Type *mktype(int line, Ty ty);
+Type *mktype(Srcloc l, Ty ty);
 Type *tydup(Type *t); /* shallow duplicate; all subtypes/members/... kept */
-Type *mktyvar(int line);
-Type *mktyparam(int line, char *name);
-Type *mktyname(int line, Node *name, Type **params, size_t nparams, Type *base);
-Type *mktyunres(int line, Node *name, Type **params, size_t nparams);
-Type *mktyarray(int line, Type *base, Node *sz);
-Type *mktyslice(int line, Type *base);
-Type *mktyidxhack(int line, Type *base);
-Type *mktyptr(int line, Type *base);
-Type *mktytuple(int line, Type **sub, size_t nsub);
-Type *mktyfunc(int line, Node **args, size_t nargs, Type *ret);
-Type *mktystruct(int line, Node **decls, size_t ndecls);
-Type *mktyunion(int line, Ucon **decls, size_t ndecls);
-Trait *mktrait(int line, Node *name, Type *param, Node **memb, size_t nmemb, Node **funcs, size_t nfuncs, int isproto);
-Type *mktylike(int line, Ty ty); /* constrains tyvar t like it was builtin ty */
+Type *mktyvar(Srcloc l);
+Type *mktyparam(Srcloc l, char *name);
+Type *mktyname(Srcloc l, Node *name, Type **params, size_t nparams, Type *base);
+Type *mktyunres(Srcloc l, Node *name, Type **params, size_t nparams);
+Type *mktyarray(Srcloc l, Type *base, Node *sz);
+Type *mktyslice(Srcloc l, Type *base);
+Type *mktyidxhack(Srcloc l, Type *base);
+Type *mktyptr(Srcloc l, Type *base);
+Type *mktytuple(Srcloc l, Type **sub, size_t nsub);
+Type *mktyfunc(Srcloc l, Node **args, size_t nargs, Type *ret);
+Type *mktystruct(Srcloc l, Node **decls, size_t ndecls);
+Type *mktyunion(Srcloc l, Ucon **decls, size_t ndecls);
+Trait *mktrait(Srcloc l, Node *name, Type *param, Node **memb, size_t nmemb, Node **funcs, size_t nfuncs, int isproto);
+Type *mktylike(Srcloc l, Ty ty); /* constrains tyvar t like it was builtin ty */
 int   istysigned(Type *t);
 int   istyunsigned(Type *t);
 int   istyfloat(Type *t);
@@ -480,35 +485,35 @@
 char *traitstr(Type *t);
 
 /* node creation */
-Node *mknode(int line, Ntype nt);
+Node *mknode(Srcloc l, Ntype nt);
 Node *mkfile(char *name);
-Node *mkuse(int line, char *use, int islocal);
-Node *mksliceexpr(int line, Node *sl, Node *base, Node *off);
-Node *mkexprl(int line, Op op, Node **args, size_t nargs);
-Node *mkexpr(int line, Op op, ...); /* NULL terminated */
-Node *mkcall(int line, Node *fn, Node **args, size_t nargs);
-Node *mkifstmt(int line, Node *cond, Node *iftrue, Node *iffalse);
-Node *mkloopstmt(int line, Node *init, Node *cond, Node *incr, Node *body);
-Node *mkiterstmt(int line, Node *elt, Node *seq, Node *body);
-Node *mkmatchstmt(int line, Node *val, Node **matches, size_t nmatches);
-Node *mkmatch(int line, Node *pat, Node *body);
-Node *mkblock(int line, Stab *scope);
-Node *mkimplstmt(int line, Node *name, Type *type, Node **impls, size_t nimpls);
-Node *mkintlit(int line, uvlong val);
-Node *mkidxinit(int line, Node *idx, Node *init);
+Node *mkuse(Srcloc l, char *use, int islocal);
+Node *mksliceexpr(Srcloc l, Node *sl, Node *base, Node *off);
+Node *mkexprl(Srcloc l, Op op, Node **args, size_t nargs);
+Node *mkexpr(Srcloc l, Op op, ...); /* NULL terminated */
+Node *mkcall(Srcloc l, Node *fn, Node **args, size_t nargs);
+Node *mkifstmt(Srcloc l, Node *cond, Node *iftrue, Node *iffalse);
+Node *mkloopstmt(Srcloc l, Node *init, Node *cond, Node *incr, Node *body);
+Node *mkiterstmt(Srcloc l, Node *elt, Node *seq, Node *body);
+Node *mkmatchstmt(Srcloc l, Node *val, Node **matches, size_t nmatches);
+Node *mkmatch(Srcloc l, Node *pat, Node *body);
+Node *mkblock(Srcloc l, Stab *scope);
+Node *mkimplstmt(Srcloc l, Node *name, Type *type, Node **impls, size_t nimpls);
+Node *mkintlit(Srcloc l, uvlong val);
+Node *mkidxinit(Srcloc l, Node *idx, Node *init);
 
-Node *mkbool(int line, int val);
-Node *mkint(int line, uint64_t val);
-Node *mkchar(int line, uint32_t val);
-Node *mkstr(int line, char *s);
-Node *mkfloat(int line, double flt);
-Node *mkfunc(int line, Node **args, size_t nargs, Type *ret, Node *body);
-Node *mkname(int line, char *name);
-Node *mknsname(int line, char *ns, char *name);
-Node *mkdecl(int line, Node *name, Type *ty);
-Node *mklbl(int line, char *lbl);
-Node *mkslice(int line, Node *base, Node *off);
-Ucon *mkucon(int line, Node *name, Type *ut, Type *uet);
+Node *mkbool(Srcloc l, int val);
+Node *mkint(Srcloc l, uint64_t val);
+Node *mkchar(Srcloc l, uint32_t val);
+Node *mkstr(Srcloc l, char *s);
+Node *mkfloat(Srcloc l, double flt);
+Node *mkfunc(Srcloc l, Node **args, size_t nargs, Type *ret, Node *body);
+Node *mkname(Srcloc l, char *name);
+Node *mknsname(Srcloc l, char *ns, char *name);
+Node *mkdecl(Srcloc l, Node *name, Type *ty);
+Node *mklbl(Srcloc l, char *lbl);
+Node *mkslice(Srcloc l, Node *base, Node *off);
+Ucon *mkucon(Srcloc l, Node *name, Type *ut, Type *uet);
 
 /* node util functions */
 char *namestr(Node *name);
@@ -600,4 +605,4 @@
 extern char **incpaths;
 extern size_t nincpaths;
 
-
+void yyerror(const char *s);
--- a/parse/specialize.c
+++ b/parse/specialize.c
@@ -42,7 +42,7 @@
         return htget(tsmap, t);
     switch (t->type) {
         case Typaram:
-            ret = mktyvar(t->line);
+            ret = mktyvar(t->loc);
             addtraits(ret, t->traits);
             htput(tsmap, t, ret);
             break;
@@ -54,11 +54,11 @@
             for (i = 0; i < t->nparam; i++) {
                 if (subst[i]->type != Typaram || hthas(tsmap, subst[i]))
                     continue;
-                tmp = mktyvar(subst[i]->line);
+                tmp = mktyvar(subst[i]->loc);
                 addtraits(tmp, subst[i]->traits);
                 htput(tsmap, subst[i], tmp);
             }
-            ret = mktyname(t->line, t->name, t->param, t->nparam, tyspecialize(t->sub[0], tsmap, delayed));
+            ret = mktyname(t->loc, t->name, t->param, t->nparam, tyspecialize(t->sub[0], tsmap, delayed));
             ret->issynth = 1;
             htput(tsmap, t, ret);
             for (i = 0; i < t->nparam; i++)
@@ -79,7 +79,7 @@
                 tmp = NULL;
                 if (ret->udecls[i]->etype)
                     tmp = tyspecialize(t->udecls[i]->etype, tsmap, delayed);
-                ret->udecls[i] = mkucon(t->line, t->udecls[i]->name, ret, tmp);
+                ret->udecls[i] = mkucon(t->loc, t->udecls[i]->name, ret, tmp);
                 ret->udecls[i]->utype = ret;
                 ret->udecls[i]->id = i;
                 ret->udecls[i]->synth = 1;
@@ -249,7 +249,7 @@
 
     if (!n)
         return NULL;
-    r = mknode(n->line, n->type);
+    r = mknode(n->loc, n->type);
     switch (n->type) {
         case Nfile:
         case Nuse:
@@ -372,7 +372,7 @@
     p += snprintf(p, end - p, "%s", n->decl.name->name.name);
     p += snprintf(p, end - p, "$%lu", strhash(s));
     free(s);
-    name = mkname(n->line, buf);
+    name = mkname(n->loc, buf);
     if (n->decl.name->name.ns)
         setns(name, n->decl.name->name.ns);
     return name;
@@ -397,7 +397,7 @@
     *name = genericname(n, to);
     d = getdcl(file->file.globls, *name);
     if (debugopt['S'])
-        printf("depth[%d] specializing [%d]%s => %s\n", stabstkoff, n->line, namestr(n->decl.name), namestr(*name));
+        printf("depth[%d] specializing [%d]%s => %s\n", stabstkoff, n->loc.line, namestr(n->decl.name), namestr(*name));
     if (d)
         return d;
     if (n->decl.trait)
@@ -405,7 +405,7 @@
     /* namespaced names need to be looked up in their correct
      * context. */
     if (n->decl.name->name.ns) {
-        ns = mkname(n->line, n->decl.name->name.ns);
+        ns = mkname(n->loc, n->decl.name->name.ns);
         st = getns(file->file.globls, ns);
         pushstab(st);
     }
@@ -414,7 +414,7 @@
     tsmap = mkht(tyhash, tyeq);
     fillsubst(tsmap, to, n->decl.type);
 
-    d = mkdecl(n->line, *name, tysubst(n->decl.type, tsmap));
+    d = mkdecl(n->loc, *name, tysubst(n->decl.type, tsmap));
     d->decl.isconst = n->decl.isconst;
     d->decl.isextern = n->decl.isextern;
     d->decl.init = specializenode(n->decl.init, tsmap);
--- a/parse/stab.c
+++ b/parse/stab.c
@@ -16,13 +16,13 @@
 typedef struct Tydefn Tydefn;
 typedef struct Traitdefn Traitdefn;
 struct Tydefn {
-    int line;
+    Srcloc loc;
     Node *name;
     Type *type;
 };
 
 struct Traitdefn {
-    int line;
+    Srcloc loc;
     Node *name;
     Trait *trait;
 };
@@ -201,7 +201,7 @@
 
     d = htget(st->dcl, s->decl.name);
     if (d)
-        fatal(s, "%s already declared (on line %d)", namestr(s->decl.name), d->line);
+        fatal(s, "%s already declared (on line %d)", namestr(s->decl.name), d->loc.line);
     forcedcl(st, s);
 }
 
@@ -229,7 +229,7 @@
     if (gettype(st, n))
         fatal(n, "Type %s already defined", tystr(gettype(st, n)));
     td = xalloc(sizeof(Tydefn));
-    td->line = n->line;
+    td->loc = n->loc;
     td->name = n;
     td->type = t;
     if (st->name)
@@ -240,7 +240,7 @@
 void putucon(Stab *st, Ucon *uc)
 {
     if (getucon(st, uc->name))
-        lfatal(uc->line, uc->file, "union constructor %s already defined", namestr(uc->name));
+        lfatal(uc->loc, "union constructor %s already defined", namestr(uc->name));
     htput(st->uc, uc->name, uc);
 }
 
@@ -253,7 +253,7 @@
     if (gettype(st, n))
         fatal(n, "Trait %s already defined as a type", namestr(n));
     td = xalloc(sizeof(Tydefn));
-    td->line = n->line;
+    td->loc = n->loc;
     td->name = n;
     td->trait = c;
     htput(st->tr, td->name, td);
@@ -303,7 +303,7 @@
 
     if (st->name)
         die("Stab %s already has namespace; Can't set to %s", namestr(st->name), name);
-    st->name = mkname(-1, name);
+    st->name = mkname(Zloc, name);
     k = htkeys(st->dcl, &nk);
     for (i = 0; i < nk; i++)
         setns(k[i], name);
--- a/parse/tok.c
+++ b/parse/tok.c
@@ -17,7 +17,7 @@
 #define End (-1)
 
 char *filename;
-int line;
+Srcloc curloc;
 Tok *curtok;
 
 /* the file contents are stored globally */
@@ -75,7 +75,7 @@
 
     t = zalloc(sizeof(Tok));
     t->type = tt;
-    t->line = line;
+    t->loc = curloc;
     return t;
 }
 
@@ -91,7 +91,7 @@
     int c;
 
     depth = 0;
-    startln = line;
+    startln = curloc.line;
     while (1) {
         c = next();
         switch (c) {
@@ -107,10 +107,10 @@
                 break;
             /* have to keep line numbers synced */
             case '\n':
-                line++;
+                curloc.line++;
                 break;
             case End:
-                lfatal(line, 0, "File ended within comment starting at line %d", startln);
+                lfatal(curloc, 0, "File ended within comment starting at line %d", startln);
                 break;
         }
         if (depth == 0)
@@ -140,7 +140,7 @@
             next();
         } else if (ignorenl && c == '\n') {
             next();
-            line++;
+            curloc.line++;
             ignorenl = 0;
         } else if (isspace(c)) {
             next();
@@ -287,7 +287,7 @@
     else if (c < 0x200000)
         charlen = 4;
     else
-        lfatal(line, 0, "invalid utf character '\\u{%x}'", c);
+        lfatal(curloc, 0, "invalid utf character '\\u{%x}'", c);
 
     encode(charbuf, charlen, c);
     for (i = 0; i < charlen; i++)
@@ -316,7 +316,7 @@
         return c - 'A' + 10;
     else if (c >= '0' && c <= '9')
         return c - '0';
-    lfatal(line, 0, "passed non-hex value '%c' to where hex was expected", c);
+    lfatal(curloc, 0, "passed non-hex value '%c' to where hex was expected", c);
     return -1;
 }
 
@@ -328,16 +328,16 @@
 
     /* we've already seen the \u */
     if (next() != '{')
-        lfatal(line, 0, "\\u escape sequence without initial '{'");
+        lfatal(curloc, 0, "\\u escape sequence without initial '{'");
     v = 0;
     while (ishexval(peek())) {
         c = next();
         v = 16*v + hexval(c);
         if (v > 0x10FFFF)
-            lfatal(line, 0, "invalid codepoint for \\u escape sequence");
+            lfatal(curloc, 0, "invalid codepoint for \\u escape sequence");
     }
     if (next() != '}')
-        lfatal(line, 0, "\\u escape sequence without ending '}'");
+        lfatal(curloc, 0, "\\u escape sequence without ending '}'");
     return v;
 }
 
@@ -361,10 +361,10 @@
         case 'x': /* arbitrary hex */
             c1 = next();
             if (!isxdigit(c1))
-                lfatal(line, 0, "expected hex digit, got %c", c1);
+                lfatal(curloc, 0, "expected hex digit, got %c", c1);
             c2 = next();
             if (!isxdigit(c2))
-                lfatal(line, 0, "expected hex digit, got %c", c1);
+                lfatal(curloc, 0, "expected hex digit, got %c", c1);
             v = 16*hexval(c1) + hexval(c2);
             break;
         case 'n': v = '\n'; break;
@@ -376,7 +376,7 @@
         case 'v': v = '\v'; break;
         case '\\': v = '\\'; break;
         case '0': v = '\0'; break;
-        default: lfatal(line, 0, "unknown escape code \\%c", c);
+        default: lfatal(curloc, 0, "unknown escape code \\%c", c);
     }
     append(buf, len, sz, v);
     return v;
@@ -400,9 +400,9 @@
         if (c == '"')
             break;
         else if (c == End)
-            lfatal(line, 0, "Unexpected EOF within string");
+            lfatal(curloc, 0, "Unexpected EOF within string");
         else if (c == '\n')
-            lfatal(line, 0, "Newlines not allowed in strings");
+            lfatal(curloc, 0, "Newlines not allowed in strings");
         else if (c == '\\')
             decode(&buf, &len, &sz);
         else
@@ -428,7 +428,7 @@
     else if ((c & 0xf8) == 0xf0)
         len = 4;
     else
-        lfatal(line, 0, "Invalid utf8 encoded character constant");
+        lfatal(curloc, 0, "Invalid utf8 encoded character constant");
 
     val = c & ((1 << (8 - len)) - 1);
     append(buf, buflen, sz, c);
@@ -435,7 +435,7 @@
     for (i = 1; i < len; i++) {
         c = next();
         if ((c & 0xc0) != 0x80)
-            lfatal(line, 0, "Invalid utf8 codepoint in character literal");
+            lfatal(curloc, 0, "Invalid utf8 codepoint in character literal");
         val = (val << 6) | (c & 0x3f);
         append(buf, buflen, sz, c);
     }
@@ -459,9 +459,9 @@
     val = 0;
     c = next();
     if (c == End)
-        lfatal(line, 0, "Unexpected EOF within char lit");
+        lfatal(curloc, 0, "Unexpected EOF within char lit");
     else if (c == '\n')
-        lfatal(line, 0, "Newlines not allowed in char lit");
+        lfatal(curloc, 0, "Newlines not allowed in char lit");
     else if (c == '\\')
         val = decode(&buf, &len, &sz);
     else
@@ -468,7 +468,7 @@
         val = readutf(c, &buf, &len, &sz);
     append(&buf, &len, &sz, '\0');
     if (next() != '\'')
-        lfatal(line, 0, "Character constant with multiple characters");
+        lfatal(curloc, 0, "Character constant with multiple characters");
 
     t = mktok(Tchrlit);
     t->chrval = val;
@@ -614,7 +614,7 @@
                   break;
         default:
                   tt = Terror;
-                  lfatal(line, 0, "Junk character %c", c);
+                  lfatal(curloc, 0, "Junk character %c", c);
                   break;
     }
     return mktok(tt);
@@ -644,10 +644,10 @@
         if (c == '.')
             isfloat = 1;
         else if (hexval(c) < 0 || hexval(c) > base)
-            lfatal(line, 0, "Integer digit '%c' outside of base %d", c, base);
+            lfatal(curloc, 0, "Integer digit '%c' outside of base %d", c, base);
         if (nbuf >= sizeof buf) {
             buf[nbuf-1] = '\0';
-            lfatal(line, 0, "number %s... too long to represent", buf);
+            lfatal(curloc, 0, "number %s... too long to represent", buf);
         }
         buf[nbuf++] = c;
     }
@@ -674,7 +674,7 @@
         switch (peek()) {
             case 'u':
                 if (unsignedval == 1)
-                    lfatal(line, 0, "Duplicate 'u' integer specifier");
+                    lfatal(curloc, 0, "Duplicate 'u' integer specifier");
                 next();
                 unsignedval = 1;
                 goto nextsuffix;
@@ -708,7 +708,7 @@
                 break;
             default:
                 if (unsignedval)
-                    lfatal(line, 0, "Unrecognized character int type specifier after 'u'");
+                    lfatal(curloc, 0, "Unrecognized character int type specifier after 'u'");
                 break;
         }
     }
@@ -762,7 +762,7 @@
     if (c == End) {
         t =  mktok(0);
     } else if (c == '\n') {
-        line++;
+        curloc.line++;
         next();
         t =  mktok(Tendln);
     } else if (isalpha(c) || c == '_' || c == '$') {
@@ -780,7 +780,7 @@
     }
 
     if (!t || t->type == Terror)
-        lfatal(line, 0, "Unable to parse token starting with %c", c);
+        lfatal(curloc, 0, "Unable to parse token starting with %c", c);
     return t;
 }
 
@@ -812,8 +812,8 @@
     }
 
     fbufsz = nread;
-    line = 1;
-    fidx = 0;
+    curloc.line = 1;
+    curloc.file = 0;
     close(fd);
     filename = strdup(file);
 }
@@ -824,4 +824,13 @@
     curtok = toknext();
     yylval.tok = curtok;
     return curtok->type;
+}
+
+void yyerror(const char *s)
+{
+    fprintf(stderr, "%s:%d: %s", filename, curloc.line, s);
+    if (curtok->str)
+        fprintf(stderr, " near \"%s\"", curtok->str);
+    fprintf(stderr, "\n");
+    exit(1);
 }
--- a/parse/type.c
+++ b/parse/type.c
@@ -27,7 +27,7 @@
 /* Built in type constraints */
 static Trait *traits[Ntypes + 1][4];
 
-Type *mktype(int line, Ty ty)
+Type *mktype(Srcloc loc, Ty ty)
 {
     Type *t;
     int i;
@@ -45,7 +45,7 @@
     t = zalloc(sizeof(Type));
     t->type = ty;
     t->tid = ntypes++;
-    t->line = line;
+    t->loc = loc;
     tytab = xrealloc(tytab, ntypes*sizeof(Type*));
     tytab[t->tid] = NULL;
     types = xrealloc(types, ntypes*sizeof(Type*));
@@ -67,7 +67,7 @@
 {
     Type *r;
 
-    r = mktype(t->line, t->type);
+    r = mktype(t->loc, t->type);
     r->resolved = 0; /* re-resolving doesn't hurt */
     r->fixed = 0; /* re-resolving doesn't hurt */
 
@@ -99,12 +99,12 @@
  * Creates a Tyvar with the same
  * constrants as the 'like' type
  */
-Type *mktylike(int line, Ty like)
+Type *mktylike(Srcloc loc, Ty like)
 {
     Type *t;
     int i;
 
-    t = mktyvar(line);
+    t = mktyvar(loc);
     for (i = 0; traits[like][i]; i++)
         settrait(t, traits[like][i]);
     return t;
@@ -111,7 +111,7 @@
 }
 
 /* steals memb, funcs */
-Trait *mktrait(int line, Node *name, Type *param, Node **memb, size_t nmemb, Node **funcs, size_t nfuncs, int isproto)
+Trait *mktrait(Srcloc loc, Node *name, Type *param, Node **memb, size_t nmemb, Node **funcs, size_t nfuncs, int isproto)
 {
     Trait *t;
 
@@ -131,29 +131,29 @@
     return t;
 }
 
-Type *mktyvar(int line)
+Type *mktyvar(Srcloc loc)
 {
     Type *t;
 
-    t = mktype(line, Tyvar);
+    t = mktype(loc, Tyvar);
     return t;
 }
 
-Type *mktyparam(int line, char *name)
+Type *mktyparam(Srcloc loc, char *name)
 {
     Type *t;
 
-    t = mktype(line, Typaram);
+    t = mktype(loc, Typaram);
     t->pname = strdup(name);
     return t;
 }
 
-Type *mktyunres(int line, Node *name, Type **arg, size_t narg)
+Type *mktyunres(Srcloc loc, Node *name, Type **arg, size_t narg)
 {
     Type *t;
 
     /* resolve it in the type inference stage */
-    t = mktype(line, Tyunres);
+    t = mktype(loc, Tyunres);
     t->name = name;
     t->arg = arg;
     t->narg = narg;
@@ -160,11 +160,11 @@
     return t;
 }
 
-Type *mktyname(int line, Node *name, Type **param, size_t nparam, Type *base)
+Type *mktyname(Srcloc loc, Node *name, Type **param, size_t nparam, Type *base)
 {
     Type *t;
 
-    t = mktype(line, Tyname);
+    t = mktype(loc, Tyname);
     t->name = name;
     t->nsub = 1;
     t->traits = bsdup(base->traits);
@@ -175,11 +175,11 @@
     return t;
 }
 
-Type *mktyarray(int line, Type *base, Node *sz)
+Type *mktyarray(Srcloc loc, Type *base, Node *sz)
 {
     Type *t;
 
-    t = mktype(line, Tyarray);
+    t = mktype(loc, Tyarray);
     t->nsub = 1;
     t->nmemb = 1; /* the size is a "member" */
     t->sub = xalloc(sizeof(Type*));
@@ -189,11 +189,11 @@
     return t;
 }
 
-Type *mktyslice(int line, Type *base)
+Type *mktyslice(Srcloc loc, Type *base)
 {
     Type *t;
 
-    t = mktype(line, Tyslice);
+    t = mktype(loc, Tyslice);
     t->nsub = 1;
     t->sub = xalloc(sizeof(Type*));
     t->sub[0] = base;
@@ -200,11 +200,11 @@
     return t;
 }
 
-Type *mktyidxhack(int line, Type *base)
+Type *mktyidxhack(Srcloc loc, Type *base)
 {
     Type *t;
 
-    t = mktype(line, Tyvar);
+    t = mktype(loc, Tyvar);
     t->nsub = 1;
     t->sub = xalloc(sizeof(Type*));
     t->sub[0] = base;
@@ -211,11 +211,11 @@
     return t;
 }
 
-Type *mktyptr(int line, Type *base)
+Type *mktyptr(Srcloc loc, Type *base)
 {
     Type *t;
 
-    t = mktype(line, Typtr);
+    t = mktype(loc, Typtr);
     t->nsub = 1;
     t->sub = xalloc(sizeof(Type*));
     t->sub[0] = base;
@@ -222,12 +222,12 @@
     return t;
 }
 
-Type *mktytuple(int line, Type **sub, size_t nsub)
+Type *mktytuple(Srcloc loc, Type **sub, size_t nsub)
 {
     Type *t;
     size_t i;
 
-    t = mktype(line, Tytuple);
+    t = mktype(loc, Tytuple);
     t->nsub = nsub;
     t->sub = xalloc(nsub*sizeof(Type));
     for (i = 0; i < nsub; i++)
@@ -235,12 +235,12 @@
     return t;
 }
 
-Type *mktyfunc(int line, Node **args, size_t nargs, Type *ret)
+Type *mktyfunc(Srcloc loc, Node **args, size_t nargs, Type *ret)
 {
     Type *t;
     size_t i;
 
-    t = mktype(line, Tyfunc);
+    t = mktype(loc, Tyfunc);
     t->nsub = nargs + 1;
     t->sub = xalloc((1 + nargs)*sizeof(Type));
     t->sub[0] = ret;
@@ -249,11 +249,11 @@
     return t;
 }
 
-Type *mktystruct(int line, Node **decls, size_t ndecls)
+Type *mktystruct(Srcloc loc, Node **decls, size_t ndecls)
 {
     Type *t;
 
-    t = mktype(line, Tystruct);
+    t = mktype(loc, Tystruct);
     t->nsub = 0;
     t->nmemb = ndecls;
     t->sdecls = memdup(decls, ndecls*sizeof(Node *));
@@ -260,11 +260,11 @@
     return t;
 }
 
-Type *mktyunion(int line, Ucon **decls, size_t ndecls)
+Type *mktyunion(Srcloc loc, Ucon **decls, size_t ndecls)
 {
     Type *t;
 
-    t = mktype(line, Tyunion);
+    t = mktype(loc, Tyunion);
     t->nmemb = ndecls;
     t->udecls = decls;
     return t;
@@ -698,7 +698,7 @@
 /* this must be done after all the types are created, otherwise we will
  * clobber the memoized bunch of types with the type params. */
 #define Tc(c, n) \
-    mktrait(-1, mkname(-1, n), NULL, NULL, 0, NULL, 0, 0);
+    mktrait(Zloc, mkname(Zloc, n), NULL, NULL, 0, NULL, 0, 0);
 #include "trait.def"
 #undef Tc
 
@@ -736,9 +736,9 @@
  * constraints, otherwise they will have no constraints set on them. */
 #define Ty(t, n) \
     if (t != Ntypes) {\
-      ty = mktype(-1, t); \
+      ty = mktype(Zloc, t); \
       if (n) { \
-          puttype(st, mkname(-1, n), ty); \
+          puttype(st, mkname(Zloc, n), ty); \
       } \
     }
 #include "types.def"
--- a/parse/use.c
+++ b/parse/use.c
@@ -99,7 +99,7 @@
 
 static void wrucon(FILE *fd, Ucon *uc)
 {
-    wrint(fd, uc->line);
+    wrint(fd, uc->loc.line);
     wrint(fd, uc->id);
     wrbool(fd, uc->synth);
     pickle(fd, uc->name);
@@ -122,7 +122,9 @@
     id = rdint(fd);
     synth = rdbool(fd);
     name = unpickle(fd);
-    uc = mkucon(line, name, ut, et);
+    uc = mkucon(Zloc, name, ut, et);
+    uc->loc.line = line;
+    uc->loc.file = file->file.nfiles - 1;
     if (rdbool(fd))
       rdtype(fd, &uc->etype);
     uc->id = id;
@@ -137,7 +139,7 @@
 static void wrsym(FILE *fd, Node *val)
 {
     /* sym */
-    wrint(fd, val->line);
+    wrint(fd, val->loc.line);
     pickle(fd, val->decl.name);
     wrtype(fd, val->decl.type);
 
@@ -162,7 +164,9 @@
 
     line = rdint(fd);
     name = unpickle(fd);
-    n = mkdecl(line, name, NULL);
+    n = mkdecl(Zloc, name, NULL);
+    n->loc.line = line;
+    n->loc.file = file->file.nfiles - 1;
     rdtype(fd, &n->decl.type);
 
     if (rdint(fd) == Vishidden)
@@ -285,7 +289,7 @@
 
     tid = rdint(fd);
     if (tid & Builtinmask) {
-        *dest = mktype(-1, tid & ~Builtinmask);
+        *dest = mktype(Zloc, tid & ~Builtinmask);
     } else {
         lappend(&typefixdest, &ntypefixdest, dest);
         lappend(&typefixid, &ntypefixid, itop(tid));
@@ -304,7 +308,7 @@
     Ty t;
 
     t = rdbyte(fd);
-    ty = mktype(-1, t);
+    ty = mktype(Zloc, t);
     if (rdbyte(fd) == Vishidden)
         ty->ishidden = 1;
     /* tid is generated; don't write */
@@ -377,7 +381,7 @@
     intptr_t uid;
 
     /* create an empty trait */
-    tr = mktrait(-1, NULL, NULL, NULL, 0, NULL, 0, 0);
+    tr = mktrait(Zloc, NULL, NULL, NULL, 0, NULL, 0, 0);
     uid = rdint(fd);
     tr->ishidden = rdbool(fd);
     tr->name = unpickle(fd);
@@ -407,7 +411,7 @@
         return;
     }
     wrbyte(fd, n->type);
-    wrint(fd, n->line);
+    wrint(fd, n->loc.line);
     switch (n->type) {
         case Nfile:
             wrstr(fd, n->file.files[0]);
@@ -536,8 +540,9 @@
     type = rdbyte(fd);
     if (type == Nnone)
         return NULL;
-    n = mknode(-1, type);
-    n->line = rdint(fd);
+    n = mknode(Zloc, type);
+    n->loc.line = rdint(fd);
+    n->loc.file = file->file.nfiles - 1;
     switch (n->type) {
         case Nfile:
             lappend(&n->file.files, &n->file.nfiles, rdstr(fd));
@@ -681,7 +686,7 @@
             return NULL;
     }
 
-    n = mkname(-1, pkg);
+    n = mkname(Zloc, pkg);
     if (getns(st, n)) {
         s = getns(st, n);
     } else {
@@ -724,7 +729,7 @@
             continue;
         old = htget(tydedup, t->name);
         if (old && !tyeq(t, old))
-            lfatal(t->line, t->file, "Duplicate definition of type %s on %s:%d", tystr(old), file->file.files[old->file], old->line);
+            lfatal(t->loc, "Duplicate definition of type %s on %s:%d", tystr(old), file->file.files[old->loc.file], old->loc.line);
     }
     lfree(&typefixdest, &ntypefixdest);
     lfree(&typefixid, &ntypefixid);
@@ -788,6 +793,9 @@
                 lappend(&file->file.libdeps, &file->file.nlibdeps, lib);
 foundlib:
                 break;
+            case 'F':
+                lappend(&file->file.files, &file->file.nfiles, rdstr(f));
+                break;
             case 'G':
             case 'D':
                 dcl = rdsym(f, NULL);
@@ -900,6 +908,10 @@
         wrbyte(f, 'L');
         wrstr(f, file->file.libdeps[i]);
     }
+
+    /* source file name */
+    wrbyte(f, 'F');
+    wrstr(f, file->file.files[0]);
 
     for (i = 0; i < ntypes; i++) {
         if (types[i]->vis == Visexport || types[i]->vis == Vishidden) {
--- a/parse/util.c
+++ b/parse/util.c
@@ -68,22 +68,22 @@
     va_list ap;
 
     va_start(ap, msg);
-    lfatalv(n->line, n->fid, msg, ap);
+    lfatalv(n->loc, msg, ap);
     va_end(ap);
 }
 
-void lfatal(int line, int file, char *msg, ...)
+void lfatal(Srcloc l, char *msg, ...)
 {
     va_list ap;
 
     va_start(ap, msg);
-    lfatalv(line, file, msg, ap);
+    lfatalv(l, msg, ap);
     va_end(ap);
 }
 
-void lfatalv(int line, int fid, char *msg, va_list ap)
+void lfatalv(Srcloc l, char *msg, va_list ap)
 {
-    fprintf(stdout, "%s:%d: ", file->file.files[fid], line);
+    fprintf(stdout, "%s:%d: ", file->file.files[l.file], l.line);
     vfprintf(stdout, msg, ap);
     fprintf(stdout, "\n");
     exit(1);