ref: 83246e296ea433b65b9d295b5e08fedd39ff1ab7
dir: /appl/cmd/limbo/typecheck.b/
fndecls: ref Decl; labstack: array of ref Node; maxlabdep: int; inexcept: ref Node; nexc: int; fndec: ref Decl; increfs(id: ref Decl) { for( ; id != nil; id = id.link) id.refs++; } fninline(d: ref Decl): int { left, right: ref Node; n := d.init; if(dontinline || d.inline == byte -1 || d.locals != nil || ispoly(d) || n.ty.tof.kind == Tnone || nodes(n) >= 100) return 0; n = n.right; if(n.op == Oseq && n.right == nil) n = n.left; # # inline # (a) return e; # (b) if(c) return e1; else return e2; # (c) if(c) return e1; return e2; # case(n.op){ Oret => break; Oif => right = n.right; if(right.right == nil || right.left.op != Oret || right.right.op != Oret || !tequal(right.left.left.ty, right.right.left.ty)) return 0; break; Oseq => left = n.left; right = n.right; if(left.op != Oif || left.right.right != nil || left.right.left.op != Oret || right.op != Oseq || right.right != nil || right.left.op != Oret || !tequal(left.right.left.left.ty, right.left.left.ty)) return 0; break; * => return 0; } if(occurs(d, n) || hasasgns(n)) return 0; if(n.op == Oseq){ left.right.right = right.left; n = left; right = n.right; d.init.right.right = nil; } if(n.op == Oif){ n.ty = right.ty = right.left.left.ty; right.left = right.left.left; right.right = right.right.left; d.init.right.left = mkunary(Oret, n); } return 1; } rewind(n: ref Node) { r, nn: ref Node; r = n; nn = n.left; for(n = n.right; n != nil; n = n.right){ if(n.right == nil){ r.left = nn; r.right = n.left; } else nn = mkbin(Oindex, nn, n.left); } } ckmod(n: ref Node, id: ref Decl) { t: ref Type; d, idc: ref Decl; mod: ref Node; if(id == nil) fatal("can't find function: " + nodeconv(n)); idc = nil; mod = nil; if(n.op == Oname){ idc = id; mod = id.eimport; } else if(n.op == Omdot) mod = n.left; else if(n.op == Odot){ idc = id.dot; t = n.left.ty; if(t.kind == Tref) t = t.tof; if(t.kind == Tadtpick) t = t.decl.dot.ty; d = t.decl; while(d != nil && d.link != nil) d = d.link; if(d != nil && d.timport != nil) mod = d.timport.eimport; n.right.left = mod; } if(mod != nil && mod.ty.kind != Tmodule){ nerror(n, "cannot use " + expconv(n) + " as a function reference"); return; } if(mod != nil){ if(valistype(mod)){ nerror(n, "cannot use " + expconv(n) + " as a function reference because " + expconv(mod) + " is a module interface"); return; } }else if(idc != nil && idc.dot != nil && !isimpmod(idc.dot.sym)){ nerror(n, "cannot use " + expconv(n) + " without importing " + idc.sym.name + " from a variable"); return; } if(mod != nil) modrefable(n.ty); } addref(n: ref Node) { nn: ref Node; nn = mkn(0, nil, nil); *nn = *n; n.op = Oref; n.left = nn; n.right = nil; n.decl = nil; n.ty = usetype(mktype(n.src.start, n.src.stop, Tref, nn.ty, nil)); } fnref(n: ref Node, id: ref Decl) { id.inline = byte -1; ckmod(n, id); addref(n); while(id.link != nil) id = id.link; if(ispoly(id) && encpolys(id) != nil) nerror(n, "cannot have a polymorphic adt function reference " + id.sym.name); } typecheck(checkimp: int): ref Decl { entry, d, m: ref Decl; if(errors) return nil; # # generate the set of all functions # compile one function at a time # gdecl(tree); gbind(tree); fns = array[nfns] of ref Decl; i := gcheck(tree, fns, 0); if(i != nfns) fatal("wrong number of functions found in gcheck"); maxlabdep = 0; for(i = 0; i < nfns; i++){ d = fns[i]; if(d != nil) fndec = d; if(d != nil) fncheck(d); fndec = nil; } if(errors) return nil; entry = nil; if(checkimp){ im: ref Decl; dm: ref Dlist; if(impmods == nil){ yyerror("no implementation module"); return nil; } for(im = impmods; im != nil; im = im.next){ for(dm = impdecls; dm != nil; dm = dm.next) if(dm.d.sym == im.sym) break; if(dm == nil || dm.d.ty == nil){ yyerror("no definition for implementation module "+im.sym.name); return nil; } } # # can't check the module spec until all types and imports are determined, # which happens in scheck # for(dm = impdecls; dm != nil; dm = dm.next){ im = dm.d; im.refs++; im.ty = usetype(im.ty); if(im.store != Dtype || im.ty.kind != Tmodule){ error(im.src.start, "cannot implement "+declconv(im)); return nil; } } # now check any multiple implementations impdecl = modimp(impdecls, impmods); s := enter("init", 0); for(dm = impdecls; dm != nil; dm = dm.next){ im = dm.d; for(m = im.ty.ids; m != nil; m = m.next){ m.ty = usetype(m.ty); m.refs++; if(m.sym == s && m.ty.kind == Tfn && entry == nil) entry = m; if(m.store == Dglobal || m.store == Dfn) modrefable(m.ty); if(m.store == Dtype && m.ty.kind == Tadt){ for(d = m.ty.ids; d != nil; d = d.next){ d.ty = usetype(d.ty); modrefable(d.ty); d.refs++; } } } checkrefs(im.ty.ids); } } if(errors) return nil; gsort(tree); tree = nil; return entry; } # # introduce all global declarations # also adds all fields to adts and modules # note the complications due to nested Odas expressions # gdecl(n: ref Node) { for(;;){ if(n == nil) return; if(n.op != Oseq) break; gdecl(n.left); n = n.right; } case n.op{ Oimport => importdecled(n); gdasdecl(n.right); Oadtdecl => adtdecled(n); Ocondecl => condecled(n); gdasdecl(n.right); Oexdecl => exdecled(n); Omoddecl => moddecled(n); Otypedecl => typedecled(n); Ovardecl => vardecled(n); Ovardecli => vardecled(n.left); gdasdecl(n.right); Ofunc => fndecled(n); Oas or Odas or Onothing => gdasdecl(n); * => fatal("can't deal with "+opconv(n.op)+" in gdecl"); } } # # bind all global type ids, # including those nested inside modules # this needs to be done, since we may use such # a type later in a nested scope, so if we bound # the type ids then, the type could get bound # to a nested declaration # gbind(n: ref Node) { ids: ref Decl; for(;;){ if(n == nil) return; if(n.op != Oseq) break; gbind(n.left); n = n.right; } case n.op{ Oas or Ocondecl or Odas or Oexdecl or Ofunc or Oimport or Onothing or Ovardecl or Ovardecli => break; Ofielddecl => bindtypes(n.decl.ty); Otypedecl => bindtypes(n.decl.ty); if(n.left != nil) gbind(n.left); Opickdecl => gbind(n.left); d := n.right.left.decl; bindtypes(d.ty); repushids(d.ty.ids); gbind(n.right.right); # get new ids for undefined types; propagate outwards ids = popids(d.ty.ids); if(ids != nil) installids(Dundef, ids); Oadtdecl or Omoddecl => bindtypes(n.ty); if(n.ty.polys != nil) repushids(n.ty.polys); repushids(n.ty.ids); gbind(n.left); # get new ids for undefined types; propagate outwards ids = popids(n.ty.ids); if(ids != nil) installids(Dundef, ids); if(n.ty.polys != nil) popids(n.ty.polys); * => fatal("can't deal with "+opconv(n.op)+" in gbind"); } } # # check all of the global declarations # bind all type ids referred to within types at the global level # record decls for defined functions # gcheck(n: ref Node, fns: array of ref Decl, nfns: int): int { ok, allok: int; for(;;){ if(n == nil) return nfns; if(n.op != Oseq) break; nfns = gcheck(n.left, fns, nfns); n = n.right; } case n.op{ Ofielddecl => if(n.decl.ty.eraises != nil) raisescheck(n.decl.ty); Onothing or Opickdecl => break; Otypedecl => tcycle(n.ty); Oadtdecl or Omoddecl => if(n.ty.polys != nil) repushids(n.ty.polys); repushids(n.ty.ids); if(gcheck(n.left, nil, 0)) fatal("gcheck fn decls nested in modules or adts"); if(popids(n.ty.ids) != nil) fatal("gcheck installs new ids in a module or adt"); if(n.ty.polys != nil) popids(n.ty.polys); Ovardecl => varcheck(n, 1); Ocondecl => concheck(n, 1); Oexdecl => excheck(n, 1); Oimport => importcheck(n, 1); Ovardecli => varcheck(n.left, 1); (ok, allok) = echeck(n.right, 0, 1, nil); if(ok){ if(allok) n.right = fold(n.right); globalas(n.right.left, n.right.right, allok); } Oas or Odas => (ok, allok) = echeck(n, 0, 1, nil); if(ok){ if(allok) n = fold(n); globalas(n.left, n.right, allok); } Ofunc => (ok, allok) = echeck(n.left, 0, 1, n); if(ok && n.ty.eraises != nil) raisescheck(n.ty); d : ref Decl = nil; if(ok) d = fnchk(n); fns[nfns++] = d; * => fatal("can't deal with "+opconv(n.op)+" in gcheck"); } return nfns; } # # check for unused expression results # make sure the any calculated expression has # a destination # checkused(n: ref Node): ref Node { # # only nil; and nil = nil; should have type tany # if(n.ty == tany){ if(n.op == Oname) return n; if(n.op == Oas) return checkused(n.right); fatal("line "+lineconv(n.src.start)+" checkused "+nodeconv(n)); } if(n.op == Ocall && n.left.ty.kind == Tfn && n.left.ty.tof != tnone){ n = mkunary(Oused, n); n.ty = n.left.ty; return n; } if(n.op == Ocall && isfnrefty(n.left.ty)){ if(n.left.ty.tof.tof != tnone){ n = mkunary(Oused, n); n.ty = n.left.ty; } return n; } if(isused[n.op] && (n.op != Ocall || n.left.ty.kind == Tfn)) return n; t := n.ty; if(t.kind == Tfn) nerror(n, "function "+expconv(n)+" not called"); else if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick) nerror(n, "expressions cannot have type "+typeconv(t)); else if(n.op == Otuple){ for(nn := n.left; nn != nil; nn = nn.right) checkused(nn.left); } else nwarn(n, "result of expression "+expconv(n)+" not used"); n = mkunary(Oused, n); n.ty = n.left.ty; return n; } fncheck(d: ref Decl) { n := d.init; if(debug['t']) print("typecheck tree: %s\n", nodeconv(n)); fndecls = nil; adtp := outerpolys(n.left); if(n.left.op == Odot) repushids(adtp); if(d.ty.polys != nil) repushids(d.ty.polys); repushids(d.ty.ids); labdep = 0; labstack = array[maxlabdep] of ref Node; n.right = scheck(n.right, d.ty.tof, Sother); if(labdep != 0) fatal("unbalanced label stack in fncheck"); labstack = nil; d.locals = appdecls(popids(d.ty.ids), fndecls); if(d.ty.polys != nil) popids(d.ty.polys); if(n.left.op == Odot) popids(adtp); fndecls = nil; checkrefs(d.ty.ids); checkrefs(d.ty.polys); checkrefs(d.locals); checkraises(n); d.inline = byte fninline(d); } scheck(n: ref Node, ret: ref Type, kind : int): ref Node { s: ref Sym; rok: int; top := n; last: ref Node = nil; for(; n != nil; n = n.right){ left := n.left; right := n.right; case n.op{ Ovardecl => vardecled(n); varcheck(n, 0); if (nested() && tmustzero(n.decl.ty)) decltozero(n); # else if (inloop() && tmustzero(n.decl.ty)) # decltozero(n); return top; Ovardecli => vardecled(left); varcheck(left, 0); echeck(right, 0, 0, nil); if (nested() && tmustzero(left.decl.ty)) decltozero(left); return top; Otypedecl => typedecled(n); bindtypes(n.ty); tcycle(n.ty); return top; Ocondecl => condecled(n); concheck(n, 0); return top; Oexdecl => exdecled(n); excheck(n, 0); return top; Oimport => importdecled(n); importcheck(n, 0); return top; Ofunc => fatal("scheck func"); Oscope => if (kind == Sother) kind = Sscope; pushscope(n, kind); if (left != nil) fatal("Oscope has left field"); echeck(left, 0, 0, nil); n.right = scheck(right, ret, Sother); d := popscope(); fndecls = appdecls(fndecls, d); return top; Olabel => echeck(left, 0, 0, nil); n.right = scheck(right, ret, Sother); return top; Oseq => n.left = scheck(left, ret, Sother); # next time will check n.right Oif => (rok, nil) = echeck(left, 0, 0, nil); if(rok && left.op != Onothing && left.ty != tint) nerror(n, "if conditional must be an int, not "+etconv(left)); right.left = scheck(right.left, ret, Sother); # next time will check n.right.right n = right; Ofor => (rok, nil) = echeck(left, 0, 0, nil); if(rok && left.op != Onothing && left.ty != tint) nerror(n, "for conditional must be an int, not "+etconv(left)); # # do the continue clause before the body # this reflects the ordering of declarations # pushlabel(n); right.right = scheck(right.right, ret, Sother); right.left = scheck(right.left, ret, Sloop); labdep--; if(n.decl != nil && !n.decl.refs) nwarn(n, "label "+n.decl.sym.name+" never referenced"); return top; Odo => (rok, nil) = echeck(left, 0, 0, nil); if(rok && left.op != Onothing && left.ty != tint) nerror(n, "do conditional must be an int, not "+etconv(left)); pushlabel(n); n.right = scheck(n.right, ret, Sloop); labdep--; if(n.decl != nil && !n.decl.refs) nwarn(n, "label "+n.decl.sym.name+" never referenced"); return top; Oalt or Ocase or Opick or Oexcept => pushlabel(n); case n.op{ Oalt => altcheck(n, ret); Ocase => casecheck(n, ret); Opick => pickcheck(n, ret); Oexcept => exccheck(n, ret); } labdep--; if(n.decl != nil && !n.decl.refs) nwarn(n, "label "+n.decl.sym.name+" never referenced"); return top; Oret => (rok, nil) = echeck(left, 0, 0, nil); if(!rok) return top; if(left == nil){ if(ret != tnone) nerror(n, "return of nothing from a fn of "+typeconv(ret)); }else if(ret == tnone){ if(left.ty != tnone) nerror(n, "return "+etconv(left)+" from a fn with no return type"); }else if(!tcompat(ret, left.ty, 0)) nerror(n, "return "+etconv(left)+" from a fn of "+typeconv(ret)); return top; Obreak or Ocont => s = nil; if(n.decl != nil) s = n.decl.sym; for(i := 0; i < labdep; i++){ if(s == nil || labstack[i].decl != nil && labstack[i].decl.sym == s){ if(n.op == Ocont && labstack[i].op != Ofor && labstack[i].op != Odo) continue; if(s != nil) labstack[i].decl.refs++; return top; } } nerror(n, "no appropriate target for "+expconv(n)); return top; Oexit or Onothing => return top; Oexstmt => fndec.handler = byte 1; n.left = scheck(left, ret, Sother); n.right = scheck(right, ret, Sother); return top; * => (nil, rok) = echeck(n, 0, 0, nil); if(rok) n = checkused(n); if(last == nil) return n; last.right = n; return top; } last = n; } return top; } pushlabel(n: ref Node) { s: ref Sym; if(labdep >= maxlabdep){ maxlabdep += MaxScope; labs := array[maxlabdep] of ref Node; labs[:] = labstack; labstack = labs; } if(n.decl != nil){ s = n.decl.sym; n.decl.refs = 0; for(i := 0; i < labdep; i++) if(labstack[i].decl != nil && labstack[i].decl.sym == s) nerror(n, "label " + s.name + " duplicated on line " + lineconv(labstack[i].decl.src.start)); } labstack[labdep++] = n; } varcheck(n: ref Node, isglobal: int) { t := validtype(n.ty, nil); t = topvartype(t, n.decl, isglobal, 0); last := n.left.decl; for(ids := n.decl; ids != last.next; ids = ids.next){ ids.ty = t; shareloc(ids); } if(t.eraises != nil) raisescheck(t); } concheck(n: ref Node, isglobal: int) { t: ref Type; init: ref Node; pushscope(nil, Sother); installids(Dconst, iota); (ok, allok) := echeck(n.right, 0, isglobal, nil); popscope(); init = n.right; if(!ok){ t = terror; }else{ t = init.ty; if(!tattr[t.kind].conable){ nerror(init, "cannot have a "+typeconv(t)+" constant"); allok = 0; } } last := n.left.decl; for(ids := n.decl; ids != last.next; ids = ids.next) ids.ty = t; if(!allok) return; i := 0; for(ids = n.decl; ids != last.next; ids = ids.next){ if(ok){ iota.init.c.val = big i; ids.init = dupn(0, nosrc, init); if(!varcom(ids)) ok = 0; } i++; } } exname(d: ref Decl): string { s := ""; m: ref Sym; if(d.dot != nil) m = d.dot.sym; else if(impmods != nil) m = impmods.sym; if(m != nil) s += m.name+"."; if(fndec != nil) s += fndec.sym.name+"."; s += string (scope-ScopeGlobal)+"."+d.sym.name; return s; } excheck(n: ref Node, isglobal: int) { t: ref Type; ids, last: ref Decl; t = validtype(n.ty, nil); t = topvartype(t, n.decl, isglobal, 0); last = n.left.decl; for(ids = n.decl; ids != last.next; ids = ids.next){ ids.ty = t; ids.init = mksconst(n.src, enterstring(exname(ids))); # ids.init = mksconst(n.src, enterstring(ids.sym.name)); } } importcheck(n: ref Node, isglobal: int) { (ok, nil) := echeck(n.right, 1, isglobal, nil); if(!ok) return; m := n.right; if(m.ty.kind != Tmodule || m.op != Oname){ nerror(n, "cannot import from "+etconv(m)); return; } last := n.left.decl; for(id := n.decl; id != last.next; id = id.next){ v := namedot(m.ty.ids, id.sym); if(v == nil){ error(id.src.start, id.sym.name+" is not a member of "+expconv(m)); id.store = Dwundef; continue; } id.store = v.store; v.ty = validtype(v.ty, nil); id.ty = t := v.ty; if(id.store == Dtype && t.decl != nil){ id.timport = t.decl.timport; t.decl.timport = id; } id.init = v.init; id.importid = v; id.eimport = m; } } rewcall(n: ref Node, d: ref Decl): ref Decl { # put original function back now we're type checked while(d.link != nil) d = d.link; if(n.op == Odot) n.right.decl = d; else if(n.op == Omdot){ n.right.right.decl = d; n.right.right.ty = d.ty; } else fatal("bad op in Ocall rewcall"); n.ty = n.right.ty = d.ty; d.refs++; usetype(d.ty); return d; } isfnrefty(t: ref Type): int { return t.kind == Tref && t.tof.kind == Tfn; } isfnref(d: ref Decl): int { case(d.store){ Dglobal or Darg or Dlocal or Dfield or Dimport => return isfnrefty(d.ty); } return 0; } tagopt: int; # # annotate the expression with types # echeck(n: ref Node, typeok, isglobal: int, par: ref Node): (int, int) { tg, id, callee: ref Decl; t, tt: ref Type; ok, allok, max, nocheck, kidsok: int; ok = allok = 1; if(n == nil) return (1, 1); if(n.op == Oseq){ for( ; n != nil && n.op == Oseq; n = n.right){ (okl, allokl) := echeck(n.left, typeok == 2, isglobal, n); ok &= okl; allok &= allokl; n.ty = tnone; } if(n == nil) return (ok, allok); } left := n.left; right := n.right; nocheck = 0; if(n.op == Odot || n.op == Omdot || n.op == Ocall || n.op == Oref || n.op == Otagof || n.op == Oindex) nocheck = 1; if(n.op != Odas # special case && n.op != Oload) # can have better error recovery (ok, allok) = echeck(left, nocheck, isglobal, n); if(n.op != Odas # special case && n.op != Odot # special check && n.op != Omdot # special check && n.op != Ocall # can have better error recovery && n.op != Oindex){ (okr, allokr) := echeck(right, 0, isglobal, n); ok &= okr; allok &= allokr; } if(!ok){ n.ty = terror; return (0, 0); } case n.op{ Odas => (ok, allok) = echeck(right, 0, isglobal, n); if(!ok) right.ty = terror; if(!isglobal && !dasdecl(left)){ ok = 0; }else if(!specific(right.ty) || !declasinfer(left, right.ty)){ nerror(n, "cannot declare "+expconv(left)+" from "+etconv(right)); declaserr(left); ok = 0; } if(right.ty.kind == Texception) left.ty = n.ty = mkextuptype(right.ty); else{ left.ty = n.ty = right.ty; usedty(n.ty); } if (nested() && tmustzero(left.ty)) decltozero(left); return (ok, allok & ok); Oseq or Onothing => n.ty = tnone; Owild => n.ty = tint; Ocast => t = usetype(n.ty); n.ty = t; tt = left.ty; if(tcompat(t, tt, 0)){ left.ty = t; break; } if(tt.kind == Tarray){ if(tt.tof == tbyte && t == tstring) break; }else if(t.kind == Tarray){ if(t.tof == tbyte && tt == tstring) break; }else if(casttab[tt.kind][t.kind]){ break; } nerror(n, "cannot make a "+typeconv(n.ty)+" from "+etconv(left)); return (0, 0); Ochan => n.ty = usetype(n.ty); if(left != nil && left.ty.kind != Tint){ nerror(n, "channel size "+etconv(left)+" is not an int"); return (0, 0); } Oload => n.ty = usetype(n.ty); (nil, kidsok) = echeck(left, 0, isglobal, n); if(n.ty.kind != Tmodule){ nerror(n, "cannot load a "+typeconv(n.ty)); return (0, 0); } if(!kidsok){ allok = 0; break; } if(left.ty != tstring){ nerror(n, "cannot load a module from "+etconv(left)); allok = 0; break; } if(n.ty.tof.decl.refs != 0) n.ty.tof.decl.refs++; n.ty.decl.refs++; usetype(n.ty.tof); Oref => t = left.ty; if(t.kind != Tadt && t.kind != Tadtpick && t.kind != Tfn && t.kind != Ttuple){ nerror(n, "cannot make a ref from "+etconv(left)); return (0, 0); } if(!tagopt && t.kind == Tadt && t.tags != nil && valistype(left)){ nerror(n, "instances of ref "+expconv(left)+" must be qualified with a pick tag"); return (0, 0); } if(t.kind == Tadtpick) t.tof = usetype(t.tof); n.ty = usetype(mktype(n.src.start, n.src.stop, Tref, t, nil)); Oarray => max = 0; if(right != nil){ max = assignindices(n); if(max < 0) return (0, 0); if(!specific(right.left.ty)){ nerror(n, "type for array not specific"); return (0, 0); } n.ty = mktype(n.src.start, n.src.stop, Tarray, right.left.ty, nil); } n.ty = usetype(n.ty); if(left.op == Onothing) n.left = left = mkconst(n.left.src, big max); if(left.ty.kind != Tint){ nerror(n, "array size "+etconv(left)+" is not an int"); return (0, 0); } Oelem => n.ty = right.ty; Orange => if(left.ty != right.ty || left.ty != tint && left.ty != tstring){ nerror(left, "range "+etconv(left)+" to "+etconv(right)+" is not an int or string range"); return (0, 0); } n.ty = left.ty; Oname => id = n.decl; if(id == nil){ nerror(n, "name with no declaration"); return (0, 0); } if(id.store == Dunbound){ s := id.sym; id = s.decl; if(id == nil) id = undefed(n.src, s); # save a little space s.unbound = nil; n.decl = id; id.refs++; } n.ty = id.ty = usetype(id.ty); case id.store{ Dfn or Dglobal or Darg or Dlocal or Dimport or Dfield or Dtag => break; Dunbound => fatal("unbound symbol found in echeck"); Dundef => nerror(n, id.sym.name+" is not declared"); id.store = Dwundef; return (0, 0); Dwundef => return (0, 0); Dconst => if(id.init == nil){ nerror(n, id.sym.name+"'s value cannot be determined"); id.store = Dwundef; return (0, 0); } Dtype => if(typeok) break; nerror(n, declconv(id)+" is not a variable"); return (0, 0); * => fatal("echeck: unknown symbol storage"); } if(n.ty == nil){ nerror(n, declconv(id)+"'s type is not fully defined"); id.store = Dwundef; return (0, 0); } if(id.importid != nil && valistype(id.eimport) && id.store != Dconst && id.store != Dtype && id.store != Dfn){ nerror(n, "cannot use "+expconv(n)+" because "+expconv(id.eimport)+" is a module interface"); return (0, 0); } if(n.ty.kind == Texception && !int n.ty.cons && par != nil && par.op != Oraise && par.op != Odot){ nn := mkn(0, nil, nil); *nn = *n; n.op = Ocast; n.left = nn; n.decl = nil; n.ty = usetype(mkextuptype(n.ty)); } # function name as function reference if(id.store == Dfn && (par == nil || (par.op != Odot && par.op != Omdot && par.op != Ocall && par.op != Ofunc))) fnref(n, id); Oconst => if(n.ty == nil){ nerror(n, "no type in "+expconv(n)); return (0, 0); } Oas => t = right.ty; if(t.kind == Texception) t = mkextuptype(t); if(!tcompat(left.ty, t, 1)){ nerror(n, "type clash in "+etconv(left)+" = "+etconv(right)); return (0, 0); } if(t == tany) t = left.ty; n.ty = t; left.ty = t; if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick) if(left.ty.kind != Tadtpick || right.ty.kind != Tadtpick) nerror(n, "expressions cannot have type "+typeconv(t)); if(left.ty.kind == Texception){ nerror(n, "cannot assign to an exception"); return (0, 0); } if(islval(left)) break; return (0, 0); Osnd => if(left.ty.kind != Tchan){ nerror(n, "cannot send on "+etconv(left)); return (0, 0); } if(!tcompat(left.ty.tof, right.ty, 0)){ nerror(n, "type clash in "+etconv(left)+" <-= "+etconv(right)); return (0, 0); } t = right.ty; if(t == tany) t = left.ty.tof; n.ty = t; Orcv => t = left.ty; if(t.kind == Tarray) t = t.tof; if(t.kind != Tchan){ nerror(n, "cannot receive on "+etconv(left)); return (0, 0); } if(left.ty.kind == Tarray) n.ty = usetype(mktype(n.src.start, n.src.stop, Ttuple, nil, mkids(n.src, nil, tint, mkids(n.src, nil, t.tof, nil)))); else n.ty = t.tof; Ocons => if(right.ty.kind != Tlist && right.ty != tany){ nerror(n, "cannot :: to "+etconv(right)); return (0, 0); } n.ty = right.ty; if(right.ty == tany) n.ty = usetype(mktype(n.src.start, n.src.stop, Tlist, left.ty, nil)); else if(!tcompat(right.ty.tof, left.ty, 0)){ t = tparent(right.ty.tof, left.ty); if(!tcompat(t, left.ty, 0)){ nerror(n, "type clash in "+etconv(left)+" :: "+etconv(right)); return (0, 0); } else n.ty = usetype(mktype(n.src.start, n.src.stop, Tlist, t, nil)); } Ohd or Otl => if(left.ty.kind != Tlist || left.ty.tof == nil){ nerror(n, "cannot "+opconv(n.op)+" "+etconv(left)); return (0, 0); } if(n.op == Ohd) n.ty = left.ty.tof; else n.ty = left.ty; Otuple => n.ty = usetype(mktype(n.src.start, n.src.stop, Ttuple, nil, tuplefields(left))); Ospawn => if(left.op != Ocall || left.left.ty.kind != Tfn && !isfnrefty(left.left.ty)){ nerror(left, "cannot spawn "+expconv(left)); return (0, 0); } if(left.ty != tnone){ nerror(left, "cannot spawn functions which return values, such as "+etconv(left)); return (0, 0); } Oraise => if(left.op == Onothing){ if(inexcept == nil){ nerror(n, expconv(n)+": empty raise not in exception handler"); return (0, 0); } n.left = dupn(1, n.src, inexcept); break; } if(left.ty != tstring && left.ty.kind != Texception){ nerror(n, expconv(n)+": raise argument "+etconv(left)+" is not a string or exception"); return (0, 0); } if((left.op != Ocall || left.left.ty.kind == Tfn) && left.ty.ids != nil && int left.ty.cons){ nerror(n, "too few exception arguments"); return (0, 0); } Ocall => (nil, kidsok) = echeck(right, 0, isglobal, nil); t = left.ty; usedty(t); pure := 1; if(t.kind == Tref){ pure = 0; t = t.tof; } if(t.kind != Tfn) return callcast(n, kidsok, allok); n.ty = t.tof; if(!kidsok){ allok = 0; break; } # # get the name to call and any associated module # mod: ref Node = nil; callee = nil; id = nil; tt = nil; if(left.op == Odot){ callee = left.right.decl; id = callee.dot; right = passimplicit(left, right); n.right = right; tt = left.left.ty; if(tt.kind == Tref) tt = tt.tof; ttt := tt; if(tt.kind == Tadtpick) ttt = tt.decl.dot.ty; dd := ttt.decl; while(dd != nil && dd.link != nil) dd = dd.link; if(dd != nil && dd.timport != nil) mod = dd.timport.eimport; # # stash the import module under a rock, # because we won't be able to get it later # after scopes are popped # left.right.left = mod; }else if(left.op == Omdot){ if(left.right.op == Odot){ callee = left.right.right.decl; right = passimplicit(left.right, right); n.right = right; tt = left.right.left.ty; if(tt.kind == Tref) tt = tt.tof; }else callee = left.right.decl; mod = left.left; }else if(left.op == Oname){ callee = left.decl; id = callee; mod = id.eimport; }else if(pure){ nerror(left, expconv(left)+" is not a function name"); allok = 0; break; } if(pure && callee == nil) fatal("can't find called function: "+nodeconv(left)); if(callee != nil && callee.store != Dfn && !isfnref(callee)){ nerror(left, expconv(left)+" is not a function"); allok = 0; break; } if(mod != nil && mod.ty.kind != Tmodule){ nerror(left, "cannot call "+expconv(left)); allok = 0; break; } if(mod != nil){ if(valistype(mod)){ nerror(left, "cannot call "+expconv(left)+" because "+expconv(mod)+" is a module interface"); allok = 0; break; } }else if(id != nil && id.dot != nil && !isimpmod(id.dot.sym)){ nerror(left, "cannot call "+expconv(left)+" without importing "+id.sym.name+" from a variable"); allok = 0; break; } if(mod != nil) modrefable(left.ty); if(callee != nil && callee.store != Dfn) callee = nil; if(t.varargs != byte 0){ t = mkvarargs(left, right); if(left.ty.kind == Tref) left.ty = usetype(mktype(t.src.start, t.src.stop, Tref, t, nil)); else left.ty = t; } else if(ispoly(callee) || isfnrefty(left.ty) && left.ty.tof.polys != nil){ unifysrc = n.src; if(!argncompat(n, t.ids, right)){ allok = 0; break; } (okp, tp) := tunify(left.ty, calltype(left.ty, right, n.ty)); if(!okp){ nerror(n, "function call type mismatch (" + typeconv(left.ty)+" vs "+typeconv(calltype(left.ty, right, n.ty))+")"); allok = 0; } else{ (n.ty, tp) = expandtype(n.ty, nil, nil, tp); n.ty = usetype(n.ty); if(ispoly(callee) && tt != nil && (tt.kind == Tadt || tt.kind == Tadtpick) && int (tt.flags&INST)) callee = rewcall(left, callee); n.right = passfns(n.src, callee, left, right, tt, tp); } } else if(!argcompat(n, t.ids, right)) allok = 0; Odot => t = left.ty; if(t.kind == Tref) t = t.tof; case t.kind{ Tadt or Tadtpick or Ttuple or Texception or Tpoly => id = namedot(t.ids, right.decl.sym); if(id == nil){ id = namedot(t.tags, right.decl.sym); if(id != nil && !valistype(left)){ nerror(n, expconv(left)+" is not a type"); return (0, 0); } } if(id == nil){ id = namedot(t.polys, right.decl.sym); if(id != nil && !valistype(left)){ nerror(n, expconv(left)+" is not a type"); return (0, 0); } } if(id == nil && t.kind == Tadtpick) id = namedot(t.decl.dot.ty.ids, right.decl.sym); if(id == nil){ for(tg = t.tags; tg != nil; tg = tg.next){ id = namedot(tg.ty.ids, right.decl.sym); if(id != nil) break; } if(id != nil){ nerror(n, "cannot yet index field "+right.decl.sym.name+" of "+etconv(left)); return (0, 0); } } if(id == nil) break; if(id.store == Dfield && valistype(left)){ nerror(n, expconv(left)+" is not a value"); return (0, 0); } id.ty = validtype(id.ty, t.decl); id.ty = usetype(id.ty); break; * => nerror(left, etconv(left)+" cannot be qualified with ."); return (0, 0); } if(id == nil){ nerror(n, expconv(right)+" is not a member of "+etconv(left)); return (0, 0); } if(id.ty == tunknown){ nerror(n, "illegal forward reference to "+expconv(n)); return (0, 0); } increfs(id); right.decl = id; n.ty = id.ty; if((id.store == Dconst || id.store == Dtag) && hasside(left, 1)) nwarn(left, "result of expression "+etconv(left)+" ignored"); # function name as function reference if(id.store == Dfn && (par == nil || (par.op != Omdot && par.op != Ocall && par.op != Ofunc))) fnref(n, id); Omdot => t = left.ty; if(t.kind != Tmodule){ nerror(left, etconv(left)+" cannot be qualified with ->"); return (0, 0); } id = nil; if(right.op == Oname){ id = namedot(t.ids, right.decl.sym); }else if(right.op == Odot){ (ok, kidsok) = echeck(right, 0, isglobal, n); allok &= kidsok; if(!ok) return (0, 0); tt = right.left.ty; if(tt.kind == Tref) tt = tt.tof; if(right.ty.kind == Tfn && tt.kind == Tadt && tt.decl.dot == t.decl) id = right.right.decl; } if(id == nil){ nerror(n, expconv(right)+" is not a member of "+etconv(left)); return (0, 0); } if(id.store != Dconst && id.store != Dtype && id.store != Dtag){ if(valistype(left)){ nerror(n, expconv(left)+" is not a value"); return (0, 0); } }else if(hasside(left, 1)) nwarn(left, "result of expression "+etconv(left)+" ignored"); if(!typeok && id.store == Dtype){ nerror(n, expconv(n)+" is a type, not a value"); return (0, 0); } if(id.ty == tunknown){ nerror(n, "illegal forward reference to "+expconv(n)); return (0, 0); } id.refs++; right.decl = id; n.ty = id.ty = usetype(id.ty); if(id.store == Dglobal) modrefable(id.ty); # function name as function reference if(id.store == Dfn && (par == nil || (par.op != Ocall && par.op != Ofunc))) fnref(n, id); Otagof => n.ty = tint; t = left.ty; if(t.kind == Tref) t = t.tof; id = nil; case left.op{ Oname => id = left.decl; Odot => id = left.right.decl; Omdot => if(left.right.op == Odot) id = left.right.right.decl; } if(id != nil && id.store == Dtag || id != nil && id.store == Dtype && t.kind == Tadt && t.tags != nil) n.decl = id; else if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick) n.decl = nil; else{ nerror(n, "cannot get the tag value for "+etconv(left)); return (1, 0); } Oind => t = left.ty; if(t.kind != Tref || (t.tof.kind != Tadt && t.tof.kind != Tadtpick && t.tof.kind != Ttuple)){ nerror(n, "cannot * "+etconv(left)); return (0, 0); } n.ty = t.tof; for(tg = t.tof.tags; tg != nil; tg = tg.next) tg.ty.tof = usetype(tg.ty.tof); Oindex => if(valistype(left)){ tagopt = 1; (nil, kidsok) = echeck(right, 2, isglobal, n); tagopt = 0; if(!kidsok) return (0, 0); if((t = exptotype(n)) == nil){ nerror(n, expconv(right) + " is not a type list"); return (0, 0); } if(!typeok){ nerror(n, expconv(left) + " is not a variable"); return (0, 0); } *n = *(n.left); n.ty = usetype(t); break; } if(0 && right.op == Oseq){ # a[e1, e2, ...] # array creation to do before we allow this rewind(n); return echeck(n, typeok, isglobal, par); } t = left.ty; (nil, kidsok) = echeck(right, 0, isglobal, n); if(t.kind != Tarray && t != tstring){ nerror(n, "cannot index "+etconv(left)); return (0, 0); } if(t == tstring){ n.op = Oinds; n.ty = tint; }else{ n.ty = t.tof; } if(!kidsok){ allok = 0; break; } if(right.ty != tint){ nerror(n, "cannot index "+etconv(left)+" with "+etconv(right)); allok = 0; break; } Oslice => t = n.ty = left.ty; if(t.kind != Tarray && t != tstring){ nerror(n, "cannot slice "+etconv(left)+" with '"+subexpconv(right.left)+":"+subexpconv(right.right)+"'"); return (0, 0); } if(right.left.ty != tint && right.left.op != Onothing || right.right.ty != tint && right.right.op != Onothing){ nerror(n, "cannot slice "+etconv(left)+" with '"+subexpconv(right.left)+":"+subexpconv(right.right)+"'"); return (1, 0); } Olen => t = left.ty; n.ty = tint; if(t.kind != Tarray && t.kind != Tlist && t != tstring){ nerror(n, "len requires an array, string or list in "+etconv(left)); return (1, 0); } Ocomp or Onot or Oneg => n.ty = left.ty; usedty(n.ty); case left.ty.kind{ Tint => return (1, allok); Treal or Tfix => if(n.op == Oneg) return (1, allok); Tbig or Tbyte => if(n.op == Oneg || n.op == Ocomp) return (1, allok); } nerror(n, "cannot apply "+opconv(n.op)+" to "+etconv(left)); return (0, 0); Oinc or Odec or Opreinc or Opredec => n.ty = left.ty; case left.ty.kind{ Tint or Tbig or Tbyte or Treal => break; * => nerror(n, "cannot apply "+opconv(n.op)+" to "+etconv(left)); return (0, 0); } if(islval(left)) break; return(0, 0); Oadd or Odiv or Omul or Osub => if(mathchk(n, 1)) break; return (0, 0); Oexp or Oexpas => n.ty = left.ty; if(n.ty != tint && n.ty != tbig && n.ty != treal){ nerror(n, "exponend " + etconv(left) + " is not int or real"); return (0, 0); } if(right.ty != tint){ nerror(n, "exponent " + etconv(right) + " is not int"); return (0, 0); } if(n.op == Oexpas && !islval(left)) return (0, 0); break; # if(mathchk(n, 0)){ # if(n.ty != tint){ # nerror(n, "exponentiation operands not int"); # return (0, 0); # } # break; # } # return (0, 0); Olsh or Orsh => if(shiftchk(n)) break; return (0, 0); Oandand or Ooror => if(left.ty != tint){ nerror(n, opconv(n.op)+"'s left operand is not an int: "+etconv(left)); allok = 0; } if(right.ty != tint){ nerror(n, opconv(n.op)+"'s right operand is not an int: "+etconv(right)); allok = 0; } n.ty = tint; Oand or Omod or Oor or Oxor => if(mathchk(n, 0)) break; return (0, 0); Oaddas or Odivas or Omulas or Osubas => if(mathchk(n, 1) && islval(left)) break; return (0, 0); Olshas or Orshas => if(shiftchk(n) && islval(left)) break; return (0, 0); Oandas or Omodas or Oxoras or Ooras => if(mathchk(n, 0) && islval(left)) break; return (0, 0); Olt or Oleq or Ogt or Ogeq => if(!mathchk(n, 1)) return (0, 0); n.ty = tint; Oeq or Oneq => case left.ty.kind{ Tint or Tbig or Tbyte or Treal or Tstring or Tref or Tlist or Tarray or Tchan or Tany or Tmodule or Tfix or Tpoly => if(!tcompat(left.ty, right.ty, 0) && !tcompat(right.ty, left.ty, 0)) break; t = left.ty; if(t == tany) t = right.ty; if(t == tany) t = tint; if(left.ty == tany) left.ty = t; if(right.ty == tany) right.ty = t; n.ty = tint; usedty(n.ty); return (1, allok); } nerror(n, "cannot compare "+etconv(left)+" to "+etconv(right)); return (0, 0); Otype => if(!typeok){ nerror(n, expconv(n) + " is not a variable"); return (0, 0); } n.ty = usetype(n.ty); * => fatal("unknown op in typecheck: "+opconv(n.op)); } usedty(n.ty); return (1, allok); } # # n is syntactically a call, but n.left is not a fn # check if it's the contructor for an adt # callcast(n: ref Node, kidsok, allok: int): (int, int) { id: ref Decl; left := n.left; right := n.right; id = nil; case left.op{ Oname => id = left.decl; Omdot => if(left.right.op == Odot) id = left.right.right.decl; else id = left.right.decl; Odot => id = left.right.decl; } if(id == nil || (id.store != Dtype && id.store != Dtag && id.ty.kind != Texception)){ nerror(left, expconv(left)+" is not a function or type name"); return (0, 0); } if(id.store == Dtag) return tagcast(n, left, right, id, kidsok, allok); t := left.ty; n.ty = t; if(!kidsok) return (1, 0); if(t.kind == Tref) t = t.tof; tt := mktype(n.src.start, n.src.stop, Ttuple, nil, tuplefields(right)); if(t.kind == Tadt && tcompat(t, tt, 1)){ if(right == nil) *n = *n.left; return (1, allok); } # try an exception with args tt = mktype(n.src.start, n.src.stop, Texception, nil, tuplefields(right)); tt.cons = byte 1; if(t.kind == Texception && t.cons == byte 1 && tcompat(t, tt, 1)){ if(right == nil) *n = *n.left; return (1, allok); } # try a cast if(t.kind != Texception && right != nil && right.right == nil){ # Oseq but single expression right = right.left; n.op = Ocast; n.left = right; n.right = nil; n.ty = mkidtype(n.src, id.sym); return echeck(n, 0, 0, nil); } nerror(left, "cannot make a "+expconv(left)+" from '("+subexpconv(right)+")'"); return (0, 0); } tagcast(n, left, right: ref Node, id: ref Decl, kidsok, allok: int): (int, int) { left.ty = id.ty; if(left.op == Omdot) left.right.ty = id.ty; n.ty = id.ty; if(!kidsok) return (1, 0); id.ty.tof = usetype(id.ty.tof); if(right != nil) right.ty = id.ty.tof; tt := mktype(n.src.start, n.src.stop, Ttuple, nil, mkids(nosrc, nil, tint, tuplefields(right))); tt.ids.store = Dfield; if(tcompat(id.ty.tof, tt, 1)) return (1, allok); nerror(left, "cannot make a "+expconv(left)+" from '("+subexpconv(right)+")'"); return (0, 0); } valistype(n: ref Node): int { case n.op{ Oname => if(n.decl.store == Dtype) return 1; Omdot => return valistype(n.right); } return 0; } islval(n: ref Node): int { s := marklval(n); if(s == 1) return 1; if(s == 0) nerror(n, "cannot assign to "+expconv(n)); else circlval(n, n); return 0; } # # check to see if n is an lval # marklval(n: ref Node): int { if(n == nil) return 0; case n.op{ Oname => return storespace[n.decl.store] && n.ty.kind != Texception; #ZZZZ && n.decl.tagged == nil; Odot => if(n.right.decl.store != Dfield) return 0; if(n.right.decl.cycle != byte 0 && n.right.decl.cyc == byte 0) return -1; if(n.left.ty.kind != Tref && marklval(n.left) == 0) nwarn(n, "assignment to "+etconv(n)+" ignored"); return 1; Omdot => if(n.right.decl.store == Dglobal) return 1; return 0; Oind => for(id := n.ty.ids; id != nil; id = id.next) if(id.cycle != byte 0 && id.cyc == byte 0) return -1; return 1; Oslice => if(n.right.right.op != Onothing || n.ty == tstring) return 0; return 1; Oinds => # # make sure we don't change a string constant # case n.left.op{ Oconst => return 0; Oname => return storespace[n.left.decl.store]; Odot or Omdot => if(n.left.right.decl != nil) return storespace[n.left.right.decl.store]; } return 1; Oindex or Oindx => return 1; Otuple => for(nn := n.left; nn != nil; nn = nn.right){ s := marklval(nn.left); if(s != 1) return s; } return 1; * => return 0; } return 0; } # # n has a circular field assignment. # find it and print an error message. # circlval(n, lval: ref Node): int { if(n == nil) return 0; case n.op{ Oname => break; Odot => if(oldcycles && n.right.decl.cycle != byte 0 && n.right.decl.cyc == byte 0){ nerror(lval, "cannot assign to "+expconv(lval)+" because field '"+n.right.decl.sym.name +"' of "+expconv(n.left)+" could complete a cycle to "+expconv(n.left)); return -1; } return 1; Oind => for(id := n.ty.ids; id != nil; id = id.next){ if(oldcycles && id.cycle != byte 0 && id.cyc == byte 0){ nerror(lval, "cannot assign to "+expconv(lval)+" because field '"+id.sym.name +"' of "+expconv(n)+" could complete a cycle to "+expconv(n)); return -1; } } return 1; Oslice => if(n.right.right.op != Onothing || n.ty == tstring) return 0; return 1; Oindex or Oinds or Oindx => return 1; Otuple => for(nn := n.left; nn != nil; nn = nn.right){ s := circlval(nn.left, lval); if(s != 1) return s; } return 1; * => return 0; } return 0; } mathchk(n: ref Node, realok: int): int { lt := n.left.ty; rt := n.right.ty; if(rt != lt && !tequal(lt, rt)){ nerror(n, "type clash in "+etconv(n.left)+" "+opconv(n.op)+" "+etconv(n.right)); return 0; } n.ty = rt; case rt.kind{ Tint or Tbig or Tbyte => return 1; Tstring => case n.op{ Oadd or Oaddas or Ogt or Ogeq or Olt or Oleq => return 1; } Treal or Tfix => if(realok) return 1; } nerror(n, "cannot "+opconv(n.op)+" "+etconv(n.left)+" and "+etconv(n.right)); return 0; } shiftchk(n: ref Node): int { right := n.right; left := n.left; n.ty = left.ty; case n.ty.kind{ Tint or Tbyte or Tbig => if(right.ty.kind != Tint){ nerror(n, "shift "+etconv(right)+" is not an int"); return 0; } return 1; } nerror(n, "cannot "+opconv(n.op)+" "+etconv(left)+" by "+etconv(right)); return 0; } # # check for any tany's in t # specific(t: ref Type): int { if(t == nil) return 0; case t.kind{ Terror or Tnone or Tint or Tbig or Tstring or Tbyte or Treal or Tfn or Tadt or Tadtpick or Tmodule or Tfix => return 1; Tany => return 0; Tpoly => return 1; Tref or Tlist or Tarray or Tchan => return specific(t.tof); Ttuple or Texception => for(d := t.ids; d != nil; d = d.next) if(!specific(d.ty)) return 0; return 1; } fatal("unknown type in specific: "+typeconv(t)); return 0; } # # infer the type of all variable in n from t # n is the left-hand exp of a := exp # declasinfer(n: ref Node, t: ref Type): int { if(t.kind == Texception){ if(int t.cons) return 0; t = mkextuptype(t); } case n.op{ Otuple => if(t.kind != Ttuple && t.kind != Tadt && t.kind != Tadtpick) return 0; ok := 1; n.ty = t; n = n.left; ids := t.ids; if(t.kind == Tadtpick) ids = t.tof.ids.next; for(; n != nil && ids != nil; ids = ids.next){ if(ids.store != Dfield) continue; ok &= declasinfer(n.left, ids.ty); n = n.right; } for(; ids != nil; ids = ids.next) if(ids.store == Dfield) break; if(n != nil || ids != nil) return 0; return ok; Oname => topvartype(t, n.decl, 0, 0); if(n.decl == nildecl) return 1; n.decl.ty = t; n.ty = t; shareloc(n.decl); return 1; } fatal("unknown op in declasinfer: "+nodeconv(n)); return 0; } # # an error occured in declaring n; # set all decl identifiers to Dwundef # so further errors are squashed. # declaserr(n: ref Node) { case n.op{ Otuple => for(n = n.left; n != nil; n = n.right) declaserr(n.left); return; Oname => if(n.decl != nildecl) n.decl.store = Dwundef; return; } fatal("unknown op in declaserr: "+nodeconv(n)); } argcompat(n: ref Node, f: ref Decl, a: ref Node): int { for(; a != nil; a = a.right){ if(f == nil){ nerror(n, expconv(n.left)+": too many function arguments"); return 0; } if(!tcompat(f.ty, a.left.ty, 0)){ nerror(n, expconv(n.left)+": argument type mismatch: expected "+typeconv(f.ty)+" saw "+etconv(a.left)); return 0; } if(a.left.ty == tany) a.left.ty = f.ty; f = f.next; } if(f != nil){ nerror(n, expconv(n.left)+": too few function arguments"); return 0; } return 1; } argncompat(n: ref Node, f: ref Decl, a: ref Node): int { for(; a != nil; a = a.right){ if(f == nil){ nerror(n, expconv(n.left)+": too many function arguments"); return 0; } f = f.next; } if(f != nil){ nerror(n, expconv(n.left)+": too few function arguments"); return 0; } return 1; } # # fn is Odot(adt, methid) # pass adt implicitly if needed # if not, any side effect of adt will be ingored # passimplicit(fname, args: ref Node): ref Node { t := fname.ty; n := fname.left; if(t.ids == nil || t.ids.implicit == byte 0){ if(!isfnrefty(t) && hasside(n, 1)) nwarn(fname, "result of expression "+expconv(n)+" ignored"); return args; } if(n.op == Oname && n.decl.store == Dtype){ nerror(n, expconv(n)+" is a type and cannot be a self argument"); n = mkn(Onothing, nil, nil); n.src = fname.src; n.ty = t.ids.ty; } args = mkn(Oseq, n, args); args.src = n.src; return args; } mem(t: ref Type, d: ref Decl): int { for( ; d != nil; d = d.next) if(d.ty == t) # was if(d.ty == t || tequal(d.ty, t)) return 1; return 0; } memp(t: ref Type, f: ref Decl): int { return mem(t, f.ty.polys) || mem(t, encpolys(f)); } passfns0(src: Src, fun: ref Decl, args0: ref Node, args: ref Node, a: ref Node, tp: ref Tpair, polys: ref Decl): (ref Node, ref Node) { id, idt, idf: ref Decl; sym: ref Sym; tt: ref Type; na, mod: ref Node; for(idt = polys; idt != nil; idt = idt.next){ tt = valtmap(idt.ty, tp); if(tt.kind == Tpoly && fndec != nil && !memp(tt, fndec)) error(src.start, "cannot determine the instantiated type of " + typeconv(tt)); for(idf = idt.ty.ids; idf != nil; idf = idf.next){ sym = idf.sym; (id, mod) = fnlookup(sym, tt); while(id != nil && id.link != nil) id = id.link; if(id == nil) # error flagged already continue; id.refs++; id.inline = byte -1; if(tt.kind == Tmodule){ # mod an actual parameter for(;;){ if(args0 != nil && tequal(tt, args0.left.ty)){ mod = args0.left; break; } if(args0 != nil) args0 = args0.right; } } if(mod == nil && (dot := lmodule(id)) != nil && !isimpmod(dot.sym)) error(src.start, "cannot use " + id.sym.name + " without importing " + id.dot.sym.name + " from a variable"); n := mkn(Ofnptr, mod, mkdeclname(src, id)); n.src = src; n.decl = fun; if(tt.kind == Tpoly) n.flags = byte FNPTRA; else n.flags = byte 0; na = mkn(Oseq, n, nil); if(a == nil) args = na; else a.right = na; n = mkn(Ofnptr, mod, mkdeclname(src, id)); n.src = src; n.decl = fun; if(tt.kind == Tpoly) n.flags = byte (FNPTRA|FNPTR2); else n.flags = byte FNPTR2; a = na.right = mkn(Oseq, n, nil); } if(args0 != nil) args0 = args0.right; } return (args, a); } passfns(src: Src, fun: ref Decl, left: ref Node, args: ref Node, adtt: ref Type, tp: ref Tpair): ref Node { a, args0: ref Node; a = nil; args0 = args; if(args != nil) for(a = args; a.right != nil; a = a.right) ; if(ispoly(fun)) polys := fun.ty.polys; else polys = left.ty.tof.polys; (args, a) = passfns0(src, fun, args0, args, a, tp, polys); if(adtt != nil){ if(ispoly(fun)) polys = encpolys(fun); else polys = nil; (args, a) = passfns0(src, fun, args0, args, a, adtt.tmap, polys); } return args; } # # check the types for a function with a variable number of arguments # last typed argument must be a constant string, and must use the # print format for describing arguments. # mkvarargs(n, args: ref Node): ref Type { last: ref Decl; nt := copytypeids(n.ty); n.ty = nt; f := n.ty.ids; last = nil; if(f == nil){ nerror(n, expconv(n)+"'s type is illegal"); return nt; } s := args; for(a := args; a != nil; a = a.right){ if(f == nil) break; if(!tcompat(f.ty, a.left.ty, 0)){ nerror(n, expconv(n)+": argument type mismatch: expected "+typeconv(f.ty)+" saw "+etconv(a.left)); return nt; } if(a.left.ty == tany) a.left.ty = f.ty; last = f; f = f.next; s = a; } if(f != nil){ nerror(n, expconv(n)+": too few function arguments"); return nt; } s.left = fold(s.left); s = s.left; if(s.ty != tstring || s.op != Oconst){ nerror(args, expconv(n)+": format argument "+etconv(s)+" is not a string constant"); return nt; } fmtcheck(n, s, a); va := tuplefields(a); if(last == nil) nt.ids = va; else last.next = va; return nt; } # # check that a print style format string matches it's arguments # fmtcheck(f, fmtarg, va: ref Node) { fmt := fmtarg.decl.sym; s := fmt.name; ns := 0; while(ns < len s){ c := s[ns++]; if(c != '%') continue; verb := -1; n1 := 0; n2 := 0; dot := 0; flag := 0; flags := ""; fmtstart := ns - 1; while(ns < len s && verb < 0){ c = s[ns++]; case c{ * => nerror(f, expconv(f)+": invalid character "+s[ns-1:ns]+" in format '"+s[fmtstart:ns]+"'"); return; '.' => if(dot){ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'"); return; } n1 = 1; dot = 1; continue; '*' => if(!n1) n1 = 1; else if(!n2 && dot) n2 = 1; else{ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'"); return; } if(va == nil){ nerror(f, expconv(f)+": too few arguments for format '"+s[fmtstart:ns]+"'"); return; } if(va.left.ty.kind != Tint){ nerror(f, expconv(f)+": format '"+s[fmtstart:ns]+"' incompatible with argument "+etconv(va.left)); return; } va = va.right; '0' to '9' => while(ns < len s && s[ns] >= '0' && s[ns] <= '9') ns++; if(!n1) n1 = 1; else if(!n2 && dot) n2 = 1; else{ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'"); return; } '+' or '-' or '#' or ',' or 'b' or 'u' => for(i := 0; i < flag; i++){ if(flags[i] == c){ nerror(f, expconv(f)+": duplicate flag "+s[ns-1:ns]+" in format '"+s[fmtstart:ns]+"'"); return; } } flags[flag++] = c; '%' or 'r' => verb = Tnone; 'H' => verb = Tany; 'c' => verb = Tint; 'd' or 'o' or 'x' or 'X' => verb = Tint; for(i := 0; i < flag; i++){ if(flags[i] == 'b'){ verb = Tbig; break; } } 'e' or 'f' or 'g' or 'E' or 'G' => verb = Treal; 's' or 'q' => verb = Tstring; } } if(verb != Tnone){ if(verb < 0){ nerror(f, expconv(f)+": incomplete format '"+s[fmtstart:ns]+"'"); return; } if(va == nil){ nerror(f, expconv(f)+": too few arguments for format '"+s[fmtstart:ns]+"'"); return; } ty := va.left.ty; if(ty.kind == Texception) ty = mkextuptype(ty); case verb{ Tint => case ty.kind{ Tstring or Tarray or Tref or Tchan or Tlist or Tmodule => if(c == 'x' || c == 'X') verb = ty.kind; } Tany => if(tattr[ty.kind].isptr) verb = ty.kind; } if(verb != ty.kind){ nerror(f, expconv(f)+": format '"+s[fmtstart:ns]+"' incompatible with argument "+etconv(va.left)); return; } va = va.right; } } if(va != nil) nerror(f, expconv(f)+": more arguments than formats"); } tuplefields(n: ref Node): ref Decl { h, last: ref Decl; for(; n != nil; n = n.right){ d := mkdecl(n.left.src, Dfield, n.left.ty); if(h == nil) h = d; else last.next = d; last = d; } return h; } # # make explicit indices for every element in an array initializer # return the maximum index # sort the indices and check for duplicates # assignindices(ar: ref Node): int { wild, off, q: ref Node; amax := 16r7fffffff; size := dupn(0, nosrc, ar.left); if(size.ty == tint){ size = fold(size); if(size.op == Oconst) amax = int size.c.val; } inits := ar.right; max := -1; last := -1; t := inits.left.ty; wild = nil; nlab := 0; ok := 1; for(n := inits; n != nil; n = n.right){ if(!tcompat(t, n.left.ty, 0)){ t = tparent(t, n.left.ty); if(!tcompat(t, n.left.ty, 0)){ nerror(n.left, "inconsistent types "+typeconv(t)+" and "+typeconv(n.left.ty)+" in array initializer"); return -1; } else inits.left.ty = t; } if(t == tany) t = n.left.ty; # # make up an index if there isn't one # if(n.left.left == nil) n.left.left = mkn(Oseq, mkconst(n.left.right.src, big(last + 1)), nil); for(q = n.left.left; q != nil; q = q.right){ off = q.left; if(off.ty != tint){ nerror(off, "array index "+etconv(off)+" is not an int"); ok = 0; continue; } off = fold(off); case off.op{ Owild => if(wild != nil) nerror(off, "array index * duplicated on line "+lineconv(wild.src.start)); wild = off; continue; Orange => if(off.left.op != Oconst || off.right.op != Oconst){ nerror(off, "range "+expconv(off)+" is not constant"); off = nil; }else if(off.left.c.val < big 0 || off.right.c.val >= big amax){ nerror(off, "array index "+expconv(off)+" out of bounds"); off = nil; }else last = int off.right.c.val; Oconst => last = int off.c.val; if(off.c.val < big 0 || off.c.val >= big amax){ nerror(off, "array index "+expconv(off)+" out of bounds"); off = nil; } Onothing => # get here from a syntax error off = nil; * => nerror(off, "array index "+expconv(off)+" is not constant"); off = nil; } nlab++; if(off == nil){ off = mkconst(n.left.right.src, big(last)); ok = 0; } if(last > max) max = last; q.left = off; } } # # fix up types of nil elements # for(n = inits; n != nil; n = n.right) if(n.left.ty == tany) n.left.ty = t; if(!ok) return -1; c := checklabels(inits, tint, nlab, "array index"); t = mktype(inits.src.start, inits.src.stop, Tainit, nil, nil); inits.ty = t; t.cse = c; return max + 1; } # # check the labels of a case statment # casecheck(cn: ref Node, ret: ref Type) { wild: ref Node; (rok, nil) := echeck(cn.left, 0, 0, nil); cn.right = scheck(cn.right, ret, Sother); if(!rok) return; arg := cn.left; t := arg.ty; if(t != tint && t != tbig && t != tstring){ nerror(cn, "case argument "+etconv(arg)+" is not an int or big or string"); return; } wild = nil; nlab := 0; ok := 1; for(n := cn.right; n != nil; n = n.right){ q := n.left.left; if(n.left.right.right == nil) nwarn(q, "no body for case qualifier "+expconv(q)); for(; q != nil; q = q.right){ left := fold(q.left); q.left = left; case left.op{ Owild => if(wild != nil) nerror(left, "case qualifier * duplicated on line "+lineconv(wild.src.start)); wild = left; Orange => if(left.ty != t) nerror(left, "case qualifier "+etconv(left)+" clashes with "+etconv(arg)); else if(left.left.op != Oconst || left.right.op != Oconst){ nerror(left, "case range "+expconv(left)+" is not constant"); ok = 0; } nlab++; * => if(left.ty != t){ nerror(left, "case qualifier "+etconv(left)+" clashes with "+etconv(arg)); ok = 0; }else if(left.op != Oconst){ nerror(left, "case qualifier "+expconv(left)+" is not constant"); ok = 0; } nlab++; } } } if(!ok) return; c := checklabels(cn.right, t, nlab, "case qualifier"); op := Tcase; if(t == tbig) op = Tcasel; else if(t == tstring) op = Tcasec; t = mktype(cn.src.start, cn.src.stop, op, nil, nil); cn.ty = t; t.cse = c; } # # check the labels and bodies of a pick statment # pickcheck(n: ref Node, ret: ref Type) { qs, q, w: ref Node; arg := n.left.right; (nil, allok) := echeck(arg, 0, 0, nil); if(!allok) return; t := arg.ty; if(t.kind == Tref) t = t.tof; if(arg.ty.kind != Tref || t.kind != Tadt || t.tags == nil){ nerror(arg, "pick argument "+etconv(arg)+" is not a ref adt with pick tags"); return; } argty := usetype(mktype(arg.ty.src.start, arg.ty.src.stop, Tref, t, nil)); arg = n.left.left; pushscope(nil, Sother); dasdecl(arg); arg.decl.ty = argty; arg.ty = argty; tags := array[t.decl.tag] of ref Node; w = nil; ok := 1; nlab := 0; for(qs = n.right; qs != nil; qs = qs.right){ qt : ref Node = nil; for(q = qs.left.left; q != nil; q = q.right){ left := q.left; case left.op{ Owild => # left.ty = tnone; left.ty = t; if(w != nil) nerror(left, "pick qualifier * duplicated on line "+lineconv(w.src.start)); w = left; Oname => id := namedot(t.tags, left.decl.sym); if(id == nil){ nerror(left, "pick qualifier "+expconv(left)+" is not a member of "+etconv(arg)); ok = 0; continue; } left.decl = id; left.ty = id.ty; if(tags[id.tag] != nil){ nerror(left, "pick qualifier "+expconv(left)+" duplicated on line "+lineconv(tags[id.tag].src.start)); ok = 0; } tags[id.tag] = left; nlab++; * => fatal("pickcheck can't handle "+nodeconv(q)); } if(qt == nil) qt = left; else if(!tequal(qt.ty, left.ty)) nerror(left, "type clash in pick qualifiers "+etconv(qt)+" and "+etconv(left)); } argty.tof = t; if(qt != nil) argty.tof = qt.ty; qs.left.right = scheck(qs.left.right, ret, Sother); if(qs.left.right == nil) nwarn(qs.left.left, "no body for pick qualifier "+expconv(qs.left.left)); } argty.tof = t; for(qs = n.right; qs != nil; qs = qs.right) for(q = qs.left.left; q != nil; q = q.right) q.left = fold(q.left); d := popscope(); d.refs++; if(d.next != nil) fatal("pickcheck: installing more than one id"); fndecls = appdecls(fndecls, d); if(!ok) return; c := checklabels(n.right, tint, nlab, "pick qualifier"); t = mktype(n.src.start, n.src.stop, Tcase, nil, nil); n.ty = t; t.cse = c; } exccheck(en: ref Node, ret: ref Type) { ed: ref Decl; wild: ref Node; qt: ref Type; pushscope(nil, Sother); if(en.left == nil) en.left = mkdeclname(en.src, mkids(en.src, enter(".ex"+string nexc++, 0), texception, nil)); oinexcept := inexcept; inexcept = en.left; dasdecl(en.left); en.left.ty = en.left.decl.ty = texception; ed = en.left.decl; # en.right = scheck(en.right, ret, Sother); t := tstring; wild = nil; nlab := 0; ok := 1; for(n := en.right; n != nil; n = n.right){ qt = nil; for(q := n.left.left; q != nil; q = q.right){ left := q.left; case left.op{ Owild => left.ty = texception; if(wild != nil) nerror(left, "exception qualifier * duplicated on line "+lineconv(wild.src.start)); wild = left; Orange => left.ty = tnone; nerror(left, "exception qualifier "+expconv(left)+" is illegal"); ok = 0; * => (rok, nil) := echeck(left, 0, 0, nil); if(!rok){ ok = 0; break; } left = q.left = fold(left); if(left.ty != t && left.ty.kind != Texception){ nerror(left, "exception qualifier "+etconv(left)+" is not a string or exception"); ok = 0; }else if(left.op != Oconst){ nerror(left, "exception qualifier "+expconv(left)+" is not constant"); ok = 0; } else if(left.ty != t) left.ty = mkextype(left.ty); nlab++; } if(qt == nil) qt = left.ty; else if(!tequal(qt, left.ty)) qt = texception; } if(qt != nil) ed.ty = qt; n.left.right = scheck(n.left.right, ret, Sother); if(n.left.right.right == nil) nwarn(n.left.left, "no body for exception qualifier " + expconv(n.left.left)); } ed.ty = texception; inexcept = oinexcept; if(!ok) return; c := checklabels(en.right, texception, nlab, "exception qualifier"); t = mktype(en.src.start, en.src.stop, Texcept, nil, nil); en.ty = t; t.cse = c; ed = popscope(); fndecls = appdecls(fndecls, ed); } # # check array and case labels for validity # checklabels(inits: ref Node, ctype: ref Type, nlab: int, title: string): ref Case { n, q, wild: ref Node; labs := array[nlab] of Label; i := 0; wild = nil; for(n = inits; n != nil; n = n.right){ for(q = n.left.left; q != nil; q = q.right){ case q.left.op{ Oconst => labs[i].start = q.left; labs[i].stop = q.left; labs[i++].node = n.left; Orange => labs[i].start = q.left.left; labs[i].stop = q.left.right; labs[i++].node = n.left; Owild => wild = n.left; * => fatal("bogus index in checklabels"); } } } if(i != nlab) fatal("bad label count: "+string nlab+" then "+string i); casesort(ctype, array[nlab] of Label, labs, 0, nlab); for(i = 0; i < nlab; i++){ p := labs[i].stop; if(casecmp(ctype, labs[i].start, p) > 0) nerror(labs[i].start, "unmatchable "+title+" "+expconv(labs[i].node)); for(e := i + 1; e < nlab; e++){ if(casecmp(ctype, labs[e].start, p) <= 0) nerror(labs[e].start, title+" '"+eprintlist(labs[e].node.left, " or ") +"' overlaps with '"+eprintlist(labs[e-1].node.left, " or ")+"' on line " +lineconv(p.src.start)); # # check for merging case labels # if(ctype != tint || labs[e].start.c.val != p.c.val+big 1 || labs[e].node != labs[i].node) break; p = labs[e].stop; } if(e != i + 1){ labs[i].stop = p; labs[i+1:] = labs[e:nlab]; nlab -= e - (i + 1); } } c := ref Case; c.nlab = nlab; c.nsnd = 0; c.labs = labs; c.wild = wild; return c; } symcmp(a: ref Sym, b: ref Sym): int { if(a.name < b.name) return -1; if(a.name > b.name) return 1; return 0; } matchcmp(na: ref Node, nb: ref Node): int { a := na.decl.sym; b := nb.decl.sym; la := len a.name; lb := len b.name; sa := la > 0 && a.name[la-1] == '*'; sb := lb > 0 && b.name[lb-1] == '*'; if(sa){ if(sb){ if(la == lb) return symcmp(a, b); return lb-la; } else return 1; } else{ if(sb) return -1; else{ if(na.ty == tstring){ if(nb.ty == tstring) return symcmp(a, b); else return 1; } else{ if(nb.ty == tstring) return -1; else return symcmp(a, b); } } } } casecmp(ty: ref Type, a, b: ref Node): int { if(ty == tint || ty == tbig){ if(a.c.val < b.c.val) return -1; if(a.c.val > b.c.val) return 1; return 0; } if(ty == texception) return matchcmp(a, b); return symcmp(a.decl.sym, b.decl.sym); } casesort(t: ref Type, aux, labs: array of Label, start, stop: int) { n := stop - start; if(n <= 1) return; top := mid := start + n / 2; casesort(t, aux, labs, start, top); casesort(t, aux, labs, mid, stop); # # merge together two sorted label arrays, yielding a sorted array # n = 0; base := start; while(base < top && mid < stop){ if(casecmp(t, labs[base].start, labs[mid].start) <= 0) aux[n++] = labs[base++]; else aux[n++] = labs[mid++]; } if(base < top) aux[n:] = labs[base:top]; else if(mid < stop) aux[n:] = labs[mid:stop]; labs[start:] = aux[:stop-start]; } # # binary search for the label corresponding to a given value # findlab(ty: ref Type, v: ref Node, labs: array of Label, nlab: int): int { if(nlab <= 1) return 0; m : int; l := 1; r := nlab - 1; while(l <= r){ m = (r + l) / 2; if(casecmp(ty, labs[m].start, v) <= 0) l = m + 1; else r = m - 1; } m = l - 1; if(casecmp(ty, labs[m].start, v) > 0 || casecmp(ty, labs[m].stop, v) < 0) fatal("findlab out of range"); return m; } altcheck(an: ref Node, ret: ref Type) { n, q, left, op, wild: ref Node; an.left = scheck(an.left, ret, Sother); ok := 1; nsnd := 0; nrcv := 0; wild = nil; for(n = an.left; n != nil; n = n.right){ q = n.left.right.left; if(n.left.right.right == nil) nwarn(q, "no body for alt guard "+expconv(q)); for(; q != nil; q = q.right){ left = q.left; case left.op{ Owild => if(wild != nil) nerror(left, "alt guard * duplicated on line "+lineconv(wild.src.start)); wild = left; Orange => nerror(left, "alt guard "+expconv(left)+" is illegal"); ok = 0; * => op = hascomm(left); if(op == nil){ nerror(left, "alt guard "+expconv(left)+" has no communication"); ok = 0; break; } if(op.op == Osnd) nsnd++; else nrcv++; } } } if(!ok) return; c := ref Case; c.nlab = nsnd + nrcv; c.nsnd = nsnd; c.wild = wild; an.ty = mktalt(c); } hascomm(n: ref Node): ref Node { if(n == nil) return nil; if(n.op == Osnd || n.op == Orcv) return n; r := hascomm(n.left); if(r != nil) return r; return hascomm(n.right); } raisescheck(t: ref Type) { if(t.kind != Tfn) return; n := t.eraises; for(nn := n.left; nn != nil; nn = nn.right){ (ok, nil) := echeck(nn.left, 0, 0, nil); if(ok && nn.left.ty.kind != Texception) nerror(n, expconv(nn.left) + ": illegal raises expression"); } } Elist: adt{ d: ref Decl; nxt: cyclic ref Elist; }; emerge(el1: ref Elist, el2: ref Elist): ref Elist { f: int; el, nxt: ref Elist; for( ; el1 != nil; el1 = nxt){ f = 0; for(el = el2; el != nil; el = el.nxt){ if(el1.d == el.d){ f = 1; break; } } nxt = el1.nxt; if(!f){ el1.nxt = el2; el2 = el1; } } return el2; } equals(n: ref Node): ref Elist { q, nn: ref Node; e, el: ref Elist; el = nil; for(q = n.left.left; q != nil; q = q.right){ nn = q.left; if(nn.op == Owild) return nil; if(nn.ty.kind != Texception) continue; e = ref Elist(nn.decl, el); el = e; } return el; } caught(d: ref Decl, n: ref Node): int { q, nn: ref Node; for(n = n.right; n != nil; n = n.right){ for(q = n.left.left; q != nil; q = q.right){ nn = q.left; if(nn.op == Owild) return 1; if(nn.ty.kind != Texception) continue; if(d == nn.decl) return 1; } } return 0; } raisecheck(n: ref Node, ql: ref Elist): ref Elist { exc: int; e: ref Node; el, nel, nxt: ref Elist; if(n == nil) return nil; el = nil; for(; n != nil; n = n.right){ case(n.op){ Oscope => return raisecheck(n.right, ql); Olabel or Odo => return raisecheck(n.right, ql); Oif or Ofor => return emerge(raisecheck(n.right.left, ql), raisecheck(n.right.right, ql)); Oalt or Ocase or Opick or Oexcept => exc = n.op == Oexcept; for(n = n.right; n != nil; n = n.right){ ql = nil; if(exc) ql = equals(n); el = emerge(raisecheck(n.left.right, ql), el); } return el; Oseq => el = emerge(raisecheck(n.left, ql), el); break; Oexstmt => el = raisecheck(n.left, ql); nel = nil; for( ; el != nil; el = nxt){ nxt = el.nxt; if(!caught(el.d, n.right)){ el.nxt = nel; nel = el; } } return emerge(nel, raisecheck(n.right, ql)); Oraise => e = n.left; if(e.ty != nil && e.ty.kind == Texception){ if(e.ty.cons == byte 0) return ql; if(e.op == Ocall) e = e.left; if(e.op == Omdot) e = e.right; if(e.op != Oname) fatal("exception " + nodeconv(e) + " not a name"); el = ref Elist(e.decl, nil); return el; } return nil; * => return nil; } } return el; } checkraises(n: ref Node) { f: int; d: ref Decl; e, el: ref Elist; es, nn: ref Node; el = raisecheck(n.right, nil); es = n.ty.eraises; if(es != nil){ for(nn = es.left; nn != nil; nn = nn.right){ d = nn.left.decl; f = 0; for(e = el; e != nil; e = e.nxt){ if(d == e.d){ f = 1; e.d = nil; break; } } if(!f) nwarn(n, "function " + expconv(n.left) + " does not raise " + d.sym.name + " but declared"); } } for(e = el; e != nil; e = e.nxt) if(e.d != nil) nwarn(n, "function " + expconv(n.left) + " raises " + e.d.sym.name + " but not declared"); } # sort all globals in modules now that we've finished with 'last' pointers # and before any code generation # gsort(n: ref Node) { for(;;){ if(n == nil) return; if(n.op != Oseq) break; gsort(n.left); n = n.right; } if(n.op == Omoddecl && int (n.ty.ok & OKverify)){ n.ty.ids = namesort(n.ty.ids); sizeids(n.ty.ids, 0); } }