diff options
| author | 2022-08-29 21:57:58 +0200 | |
|---|---|---|
| committer | 2022-08-29 21:57:58 +0200 | |
| commit | 2f243de6ce9402f880677a07b832c0e56a7f688d (patch) | |
| tree | 7f2844b121f52b0acbb5e03d2bdc83c51c187abc /src | |
| parent | e0385225b5a4a0d7b391d3d71c19228efe06ed04 (diff) | |
many things ,varags
Diffstat (limited to 'src')
| -rw-r--r-- | src/cffc.hff | 31 | ||||
| -rw-r--r-- | src/fmt.cff | 2 | ||||
| -rw-r--r-- | src/fold.cff | 20 | ||||
| -rw-r--r-- | src/llvm.cff | 164 | ||||
| -rw-r--r-- | src/parse.cff | 222 |
5 files changed, 291 insertions, 148 deletions
diff --git a/src/cffc.hff b/src/cffc.hff index e329965..11adba6 100644 --- a/src/cffc.hff +++ b/src/cffc.hff @@ -41,7 +41,7 @@ enum TokT : i32 { typearg, eof, } -def NUM_KEYWORDS int = TokT:NUM_KEYWORDS; +def NUM_KEYWORDS = as(int)TokT:NUM_KEYWORDS; struct Tok { t TokT, @@ -89,9 +89,9 @@ struct Type { Bool, Int struct { sgn bool }, Flo, - Ptr *Type, + Ptr *const Type, Arr struct { child *const Type, length i64 }, - Slice *Type, + Slice *const Type, Fn struct { params [#]*const Type, variadic bool, @@ -204,11 +204,14 @@ struct Expr { EUnionIni struct { var *const AggField, ex *Expr }, AggIni struct { flds [#]*const AggField, exs [#]Expr }, ArrIni struct { idxs [#]u32, exs [#]Expr, maxn i64 }, + VaStart *Expr, + VaArg *Expr, + VaCopy struct { dst *Expr, src *Expr }, + VaEnd *Expr, Stmt Block, } } - struct Defers { next *Defers, blockid int, @@ -333,6 +336,26 @@ struct Decl { myenv *Env, } +fn islvalue(ex *Expr) bool { + switch ex.u { + case Symbol decl; + return decl.u.#tag == :Let or decl.u.#tag == :Static; + case UnOp u; + return u.op == :deref; + case Index idx; + return idx.lhs.ty->is(:Ptr) or islvalue(idx.lhs); + case Dot dot; + return dot.lhs.ty->is(:Ptr) or islvalue(dot.lhs); + case BitDot bdot; + return bdot.lhs.ty->is(:Ptr) or islvalue(bdot.lhs); + case BitRaw ex; + return ex.ty->is(:Ptr) or islvalue(ex); + case EUTag; + return #t; + } + return #f; +} + def ATTR_LAX = 1u32; struct ExpanArg { diff --git a/src/fmt.cff b/src/fmt.cff index 39b9e5b..27bfe0e 100644 --- a/src/fmt.cff +++ b/src/fmt.cff @@ -204,6 +204,8 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list) } else { ps("(anonymous)"); } + case VaList; + ps("va_list") } } diff --git a/src/fold.cff b/src/fold.cff index 2e7cae3..8814d32 100644 --- a/src/fold.cff +++ b/src/fold.cff @@ -7,6 +7,12 @@ fn numcast(ex *Expr, to *const Type) void { let iu = &ex.u.IntLit; let f = &ex.u.FloLit; let b = &ex.u.BoolLit; + if to->is(:Enum) { + to = unconstify(to.u.Enum.intty); + } + if from->is(:Enum) { + from = unconstify(from.u.Enum.intty); + } switch { case to == from; // pass @@ -43,6 +49,7 @@ fn numcast(ex *Expr, to *const Type) void { case to->is(:Bool) and from->is(:Int); *b = iu.u == 0; case else; + efmt("%t <- %t\n",to,from); assert(#f, "bad numcast"); } @@ -192,7 +199,15 @@ fn findex(ex *Expr) void { } else { assert(#f,"bad"); } - +} +fn fas(ex *Expr) void { + let src = ex.u.Cast; + if !fold(src) or !(ex.ty->is(:Int) or ex.ty->is(:Flo) or ex.ty->is(:Enum) or ex.ty->is(:Bool)) { + return; + } + let ty = ex.ty; + *ex = *src; + numcast(ex, ty); } extern fn fold(ex *Expr) bool { @@ -213,6 +228,9 @@ extern fn fold(ex *Expr) bool { case :Cond; fcond(ex); + case :Cast; + fas(ex); + case :Index; findex(ex); diff --git a/src/llvm.cff b/src/llvm.cff index eb13919..1a7f24a 100644 --- a/src/llvm.cff +++ b/src/llvm.cff @@ -23,7 +23,7 @@ struct Value { } } -static ty_i1 *Type = as(*void)&outfp; // dummy +static ty_i1 Type = {}; // dummy fn gen(fmt *const u8, ...) void; extern fn genagg(ty *const Type) void { @@ -87,7 +87,7 @@ fn gen(fmt *const u8, ...) void { ap->start(fmt); fn pritype(ty *const Type) void { - if ty == ty_i1 { + if ty == &ty_i1 { gen("i1"); return; } @@ -121,7 +121,8 @@ fn gen(fmt *const u8, ...) void { } } gen(") "); - case VaList; gen("%s", g_targ.valistllvmty); + case VaList; gen("%%.type.valist"); + case else assert(#f, "type?"); } } @@ -142,7 +143,7 @@ fn gen(fmt *const u8, ...) void { fputc('"', outfp); foreach(c, _, str) { extern fn isprint(int) int; - if isprint(c) == 0 { + if isprint(c) == 0 or c == '"' or c == '\\' { fprintf(outfp, "\\%.2X", c); } else { fputc(c, outfp); @@ -344,6 +345,12 @@ fn genref(f *Fn, ex *Expr) Ref { gen("\tstore %t %v, %t %v\n", fex.ty, convert(f, ty.u.Arr.child, fex), ptr.ty, ptr); } return :Addr(tmp); + + case else + let it = genexpr(f, ex); + let tmp = gentmp(mkptrtype(ex.ty), "alloca %t", ex.ty); + gen("store %t %v, %t %v", it.ty, it, tmp.ty, tmp); + return :Addr(tmp); } assert(#f, "genref %d", ex.u.#tag); } @@ -362,7 +369,7 @@ fn genload(f *Fn, ref Ref) Value { let res = mktmp(fld.ty); switch { case dot.fld.ty->is(:Bool); - let tmp = gentmp(ty_i1, "icmp ne i%d %v, 0", fld.size, truncd); + let tmp = gentmp(&ty_i1, "icmp ne i%d %v, 0", fld.size, truncd); gen("\t%v = zext i1 %v to %t\n", res, tmp, res.ty); case dot.fld.ty->is(:Int); gen("\t%v = %s i%d %v to %t\n", res, @@ -402,21 +409,18 @@ fn convert(f *Fn, to *const Type, ex *Expr) Value { if from->is(:Enum) { from = from.u.Enum.intty; } defmacro cvt(inst) [ - (do let t = mktmp(to), + (do let $t = mktmp(to), val = genexpr(f, ex); - gen("\t%v = %s %t %v to %t\n", t, inst, from, val, to); - t; + gen("\t%v = %s %t %v to %t\n", $t, inst, from, val, to); + $t; ) ] switch { case from == to; return genexpr(f, ex); - case to->is(:Int) and from->is(:Int) and to.size == from.size and to.u.Int.sgn != from.u.Int.sgn; - return genexpr(f, ex); - case to->is(:Int) and from->is(:Int) and to.size == from.size; - return cvt("bitcast"); + return genexpr(f, ex); case to->is(:Int) and from->is(:Int) and to.size < from.size; return cvt("trunc"); @@ -454,25 +458,24 @@ fn convert(f *Fn, to *const Type, ex *Expr) Value { return cvt(to.u.Int.sgn ? "fptosi" : "fptoui"); case to->is(:Int) and from->is(:Bool); - let t0 = mktmp(ty_i1); + let t0 = mktmp(&ty_i1); gen("\t%v = icmp ne %t %v, 0\n", t0, ex.ty, genexpr(f, ex)); let t1 = mktmp(to); gen("\t%v = zext %t %v to %t\n", t1, t0.ty, t0, t1.ty); return t1; case to->is(:Bool) and from->is(:Int); - let t0 = mktmp(ty_i1); + let t0 = mktmp(&ty_i1); gen("\t%v = icmp ne %t %v, 0\n", t0, ex.ty, genexpr(f, ex)); let t1 = mktmp(to); gen("\t%v = zext %t %v to %t\n", t1, t0.ty, t0, t1.ty); return t1; - case to->is(:Ptr) and from->is(:Ptr); - let t = mktmp(to), - val = genexpr(f, ex); - gen("\t%v = bitcast %t %v to %t\n", t, from, val, to); - return t; + return genexpr(f, ex); + + case to->is(:Slice) and from->is(:Slice); + return genexpr(f, ex); case to->is(:Ptr) and from->is(:Arr); let addr = genref(f, ex).Addr; @@ -483,11 +486,9 @@ fn convert(f *Fn, to *const Type, ex *Expr) Value { case to->is(:Arr) and from->is(:Arr); return genexpr(f, ex); - - case else - efmt("%t %t\n",from,to); - assert(#f, "convert"); } + efmt("%t %t\n",from,to); + assert(#f, "convert"); } fn genblock(f *Fn, block Block) Option<Value>; @@ -537,8 +538,12 @@ fn genexpr(f *Fn, ex *Expr) Value { $t, lhs.ty.u.Ptr, lhs.ty, lhs, rhs.ty, rhs); res = $t; } else if rhs.ty->is(:Ptr) { + let $t = mktmp(type); + gen("\t%v = getelementptr %t, %t %v, %t %v\n", + $t, rhs.ty.u.Ptr, rhs.ty, rhs, rhs.ty, lhs); + res = $t; } else { - assert(#f, "bad sub"); + assert(#f, "bad ad"); } res; ) @@ -546,7 +551,7 @@ fn genexpr(f *Fn, ex *Expr) Value { defmacro gensub(type, lhs, rhs) [ (do let res Value #?; - if isnumtype(type) { + if isnumtype(type) and !lhs.ty->is(:Ptr) { let $t = mktmp(type); gen("\t%v = %s %t %v, %v\n", $t, ex.ty->is(:Flo) ? "fsub" : "sub", ex.ty, lhs, rhs); res = $t; @@ -555,10 +560,16 @@ fn genexpr(f *Fn, ex *Expr) Value { gen("\t%v = sub %t 0, %v\n", $off, rhs.ty, rhs); let $t = mktmp(type); gen("\t%v = getelementptr %t, %t %v, %t %v\n", - $t, lhs.ty.u.Ptr, lhs.ty, lhs, $off.ty, $off); + $t, lhs.ty.u.Ptr == ty_void ? ty_i8 : lhs.ty.u.Ptr, lhs.ty, lhs, $off.ty, $off); res = $t; + } else if lhs.ty->is(:Ptr) and rhs.ty->is(:Ptr) { + let $l = gentmp(ty_isize, "ptrtoint ptr %v to %t", lhs, ty_isize); + let $r = gentmp(ty_isize, "ptrtoint ptr %v to %t", rhs, ty_isize); + let $diff = gentmp(ty_isize, "sub %t %v, %v", ty_isize, $l, $r); + let $res = gentmp(ty_isize, "sdiv %t %v, %v", ty_isize, $l, $r); + return $res; } else { - assert(#f, "bad add"); + assert(#f, "bad sub"); } res; ) @@ -637,7 +648,7 @@ fn genexpr(f *Fn, ex *Expr) Value { static orid int = {}; let id = orid++; let lhs = genexpr(f, b.lhs); - let cnd = mktmp(ty_i1); + let cnd = mktmp(&ty_i1); let tmpvar = mktmp(mkptrtype(ex.ty)); let res = mktmp(ex.ty); gen("\t%v = alloca %t\n", tmpvar, ex.ty); @@ -656,7 +667,7 @@ fn genexpr(f *Fn, ex *Expr) Value { static andid int = {}; let id = andid++; let lhs = genexpr(f, b.lhs); - let cnd = mktmp(ty_i1); + let cnd = mktmp(&ty_i1); let tmpvar = mktmp(mkptrtype(ex.ty)); let res = mktmp(ex.ty); gen("\t%v = alloca %t\n", tmpvar, ex.ty); @@ -670,6 +681,25 @@ fn genexpr(f *Fn, ex *Expr) Value { gen("AndF%d: ", id); gen("\t%v = load %t, %t %v\n", res, ex.ty, tmpvar.ty, tmpvar); return res; + + case '??'; + static orid int = {}; + let id = orid++; + let lhs = genexpr(f, b.lhs); + let cnd = mktmp(&ty_i1); + let tmpvar = mktmp(mkptrtype(ex.ty)); + let res = mktmp(ex.ty); + gen("\t%v = alloca %t\n", tmpvar, ex.ty); + gen("\tstore %t %v, %t %v\n", ex.ty, lhs, tmpvar.ty, tmpvar); + gen("\t%v = icmp ne %t %v, null\n", cnd, lhs.ty, lhs); + gen("\tbr i1 %v, label %%OrT%d, label %%OrF%d\n", cnd, id, id); + gen("OrF%d: ", id); + let rhs = genexpr(f, b.rhs); + gen("\tstore %t %v, %t %v\n", ex.ty, rhs, tmpvar.ty, tmpvar); + gen("\tbr label %%OrT%d\n", cnd, id, id); + gen("OrT%d: ", id); + gen("\t%v = load %t, %t %v\n", res, ex.ty, tmpvar.ty, tmpvar); + return res; case else assert(#f, "binop? %c", b.op); @@ -678,7 +708,7 @@ fn genexpr(f *Fn, ex *Expr) Value { static condid int = {}; let id = condid++; let lhs = genexpr(f, cond.test); - let cnd = mktmp(ty_i1); + let cnd = mktmp(&ty_i1); let tmpvar = mktmp(mkptrtype(ex.ty)); let res = mktmp(ex.ty); gen("\t%v = alloca %t\n", tmpvar, ex.ty); @@ -713,7 +743,7 @@ fn genexpr(f *Fn, ex *Expr) Value { return tmp; case :not; - let tmp = mktmp(ty_i1); + let tmp = mktmp(&ty_i1); gen("\t%v = xor i1 1, %v\n", tmp, ex.ty, llvmbool(f, un.ex)); let res = mktmp(ex.ty); gen("\t%v = zext i1 %v to %t\n", res, tmp, res.ty); @@ -863,13 +893,12 @@ fn genexpr(f *Fn, ex *Expr) Value { let ty = i < fnty.params.#len ? fnty.params[i] : varargpromote(arg.ty); args[i] = convert(f, ty, arg); } - let t Value #?; + let t Value = mktmp(ex.ty); if fnty.ret->is(:Void) { - t = {ty_void}; - gen("\tcall %t %v(", call.lhs.ty, lhs); + gen("\tcall void %v(", lhs); } else { t = mktmp(ex.ty); - gen("\t%v = call %t %v(", t, call.lhs.ty, lhs); + gen("\t%v = call %t %v(", t, call.lhs.ty->is(:Ptr) ? call.lhs.ty.u.Ptr : call.lhs.ty, lhs); } foreach_ptr(arg, i, call.args) { let ty = i < fnty.params.#len ? fnty.params[i] : varargpromote(arg.ty); @@ -910,6 +939,17 @@ fn genexpr(f *Fn, ex *Expr) Value { gen("\t%v = load %t, %t %v\n", res, res.ty, t0.ty, t0); return res; + case VaStart ap; + gen("\tcall void @llvm.va_start(ptr %v)\n", genref(f, ap).Addr); + return mktmp(ty_void); + + case VaArg ap; + return gentmp(ex.ty, "va_arg ptr %v, %t", genref(f, ap).Addr, ex.ty); + + case VaEnd ap; + gen("\tcall void @llvm.va_end(ptr %v)\n", genref(f, ap).Addr); + return mktmp(ty_void); + case Stmt block; switch genblock(f, block) { case Some val; return val; @@ -923,7 +963,7 @@ fn genexpr(f *Fn, ex *Expr) Value { fn llvmbool(f *Fn, ex *Expr) Value { let v = genexpr(f, ex); - let t = mktmp(ty_i1); + let t = mktmp(&ty_i1); if v.ty->is(:Bool) or v.ty->is(:Int) { gen("\t%v = icmp ne %t %v, 0\n", t, v.ty, v); } else { @@ -951,9 +991,10 @@ fn genstmt(f *Fn, block *Block, st *Stmt) void { let nam = decl.name, ty = var.ty; gen("\t%%%s.%d = alloca %t\n", nam, var.id, ty); - if !var.ini->empty() { + switch var.ini { + case Some *ex; gen("\tstore %t %v, %t %%%s.%d\n", - ty, convert(f, ty, &var.ini.Some), mkptrtype(ty), nam, var.id); + ty, convert(f, ty, ex), mkptrtype(ty), nam, var.id); } } case If *cnd; @@ -1234,9 +1275,9 @@ extern fn llvm_genfn(externp bool, name *const u8, f *Fn) void { gen("}\n"); } -struct I64Traits { - fn hash(x i64) u32 { return x ^ 7; } - fn eq(a i64, b i64) bool { return a == b; } +struct IntTraits<T> { + fn hash(x T) u32 { return x ^ 7; } + fn eq(a T, b T) bool { return a == b; } } fn gendata(ty *const Type, ex *Expr) void { @@ -1249,6 +1290,10 @@ fn gendata(ty *const Type, ex *Expr) void { if !decl.externp { gen("@%s.%d", decl.name, f.id); } else { gen("@%s", decl.name); } return; + case Static v; + if !decl.externp { gen("@%s.%d", decl.name, v.id); + } else { gen("@%s", decl.name); } + return; } } assert(#f, "bad static addr"); @@ -1276,7 +1321,7 @@ fn gendata(ty *const Type, ex *Expr) void { case ArrIni ini; assert(ty->is(:Arr), "not arr arr ini"); - let map Map<i64, *Expr, I64Traits> = {}; + let map Map<i64, *Expr, IntTraits<i64>,> = {}; defer map->clear(); foreach (idx, i, ini.idxs) { map->put(idx, &ini.exs[i]); @@ -1295,7 +1340,36 @@ fn gendata(ty *const Type, ex *Expr) void { } } gen(" ]"); - return; + return; + + case AggIni ini; + assert(ty->is(:Agg), "not agg agg ini"); + + let map Map<u16, *Expr, IntTraits<u16>,> = {}; + defer map->clear(); + if ty.u.Agg.kind == :Struct { + foreach (fld, i, ini.flds) { + let idx = fld - ty.u.Agg.flds.#ptr; + map->put(idx, &ini.exs[i]); + } + gen("{ "); + for let i = 0z; i < ty.u.Agg.flds.#len; ++i { + gen("%t ", ty.u.Agg.flds[i].ty); + let ex **Expr = map->get(i); + if ex { + gendata(ty.u.Agg.flds[i].ty, *ex); + } else { + gen("zeroinitializer"); + } + if i < ty.u.Agg.flds.#len - 1 { + gen(", "); + } + } + gen(" }"); + } else { + assert(#f, "NYI static"); + } + return; } assert(#f, "bad static %d", ex.u.#tag); } @@ -1312,6 +1386,9 @@ extern fn llvm_genstatic(externp bool, name *const u8, var *Var) void { } extern fn llvm_fini() void { + gen("declare void @llvm.va_start(ptr)\n"); + gen("declare void @llvm.va_copy(ptr, ptr)\n"); + gen("declare void @llvm.va_end(ptr)\n"); vec_each(s, i, strs) { gen("@.str.%z = private unnamed_addr constant [%z x i8] c%S;\n", i, s.#len + 1, s); } @@ -1356,5 +1433,6 @@ extern fn llvm_init(out *FILE) void { outfp = out; gen("target triple = \"%s\"\n", g_targ.triple); gen("%%.type.opaque = type opaque\n"); + gen("%%.type.valist = type %s\n", g_targ.valistllvmty); gen("\n"); } diff --git a/src/parse.cff b/src/parse.cff index 2e6cec9..3be70cf 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -439,7 +439,7 @@ fn lex(P *Parser) Tok { case '"'; str->push('"'); case 'n'; str->push('\n'); case 'r'; str->push('\r'); case 't'; str->push('\t'); case 'v'; str->push('\v'); case 'f'; str->push('\f'); - case '0'; str->push('\0'); + case '0'; str->push('\0'); case 'e'; str->push('\e'); case 'x'; let x0 = xdigit2num(chr(P)), x1 = xdigit2num(chr(P)); @@ -1172,26 +1172,6 @@ fn exprdup(alloc *Allocator, ex Expr) *Expr { return memcpy(alloc->alloc(sizeof(ex), alignof(ex)), &ex, sizeof(ex)); } -fn islvalue(ex *Expr) bool { - switch ex.u { - case Symbol decl; - return decl.u.#tag == :Let or decl.u.#tag == :Static; - case UnOp u; - return u.op == :deref; - case Index; - return #t; - case Dot; - return #t; - case BitDot bdot; - return islvalue(bdot.lhs); - case BitRaw ex; - return islvalue(ex); - case EUTag; - return #t; - } - return #f; -} - fn parseaggini(P *Parser, loc Loc, ty *const Type) Expr { let loc = loc; let tok Tok #?; @@ -1540,9 +1520,9 @@ fn pexprimary(P *Parser) Expr { let st = block.sts; lexexpect(P, ')'); if st.#len == 0 or st[st.#len - 1].u.#tag != :Expr { - ex = { tok.loc, ty_void, :Stmt(st) }; + ex = { tok.loc, ty_void, :Stmt(block) }; } else { - ex = { tok.loc, st[st.#len - 1].u.Expr.ty, :Stmt(st) }; + ex = { tok.loc, st[st.#len - 1].u.Expr.ty, :Stmt(block) }; } } else { ex = parseexpr(P); @@ -1581,6 +1561,9 @@ fn pexprimary(P *Parser) Expr { if fld.ty == #null { fatal(P, iex.loc, "%t variant %qT is empty", ty, tok); } + if !typematchestarg(fld.ty, iex.ty) { + fatal(P, iex.loc, "bad enum union initializer (%t, expected %t)", iex.ty, fld.ty); + } } else if fld.ty { fatal(P, tok.loc, "%t variant %qT must be initialized", ty, tok); } @@ -1656,7 +1639,7 @@ fn pexpostfix(P *Parser) Expr { if !lhs.ty->is(:Ptr) and !lhs.ty->is(:Arr) and !lhs.ty->is(:Slice) { fatal(P, lhs.loc, "indexee is not array or pointer type (%t)", lhs.ty); } - if !rhs.ty->is(:Int) { + if !rhs.ty->is(:Int) and !(rhs.ty->is(:Enum) and rhs.ty.u.Enum.lax){ err(P, lhs.loc, "index expression type is not integral (%t)", rhs.ty); } @@ -1781,78 +1764,115 @@ fn pexpostfix(P *Parser) Expr { } case lexmatch(P, &tok, '->'); - let name = (tok = lexexpects(P, :ident, "method name")).u.ident; let ty = ex.ty; let exptr = #f; if (exptr = ty->is(:Ptr)) { ty = ty.u.Ptr; } - if !ty->is(:Agg) { - fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty); - } - let agg = &ty.u.Agg; - let decl *Decl = agg.decls ? envfind_noparent(agg.decls, name) : #null; - if decl == #null { - fatal(P, tok.loc, "%t has no such method %qT", ty, tok); - } - lexexpect(P, '('); - switch decl.u { - case Fn f; - let Fn = &f.ty.u.Fn; - if Fn.params.#len == 0 { - fatal(P, tok.loc, "function takes no arguments"); + let name = (tok = lexexpects(P, :ident, "method name")).u.ident; + switch ty.u { + case Agg *agg; + let decl *Decl = agg.decls ? envfind_noparent(agg.decls, name) : #null; + if decl == #null { + fatal(P, tok.loc, "%t has no such method %qT", ty, tok); } + lexexpect(P, '('); + switch decl.u { + case Fn f; + let Fn = &f.ty.u.Fn; + if Fn.params.#len == 0 { + fatal(P, tok.loc, "function takes no arguments"); + } - let args Vec<Expr> = {}; - let recv0 = Fn.params[0], recv = recv0; - let metptr = #f; - if (metptr = recv->is(:Ptr)) { - recv = recv.u.Ptr; - } - if unconstify(ty) != unconstify(recv) { - err(P, tok.loc, "method receiver type mismatch for `->%s' (%t, expected %t)", - name, ty, recv0); - } - switch { - case !exptr and !metptr; - - case exptr and !metptr; - ex = { ex.loc, ty, :UnOp{:deref, exprdup(P.alloc, ex)}}; - - case !exptr and metptr; - if !islvalue(&ex) { - err(P, tok.loc, "cannot call `->%s' by reference, lhs is not an lvalue", name); - } else if ty.konst and !recv.konst { - err(P, tok.loc, "constness mismatch: method takes %t but got %t", recv0, ex.ty); + let args Vec<Expr> = {}; + let recv0 = Fn.params[0], recv = recv0; + let metptr = #f; + if (metptr = recv->is(:Ptr)) { + recv = recv.u.Ptr; + } + if unconstify(ty) != unconstify(recv) { + err(P, tok.loc, "method receiver type mismatch for `->%s' (%t, expected %t)", + name, ty, recv0); + } + switch { + case !exptr and !metptr; + + case exptr and !metptr; + ex = { ex.loc, ty, :UnOp{:deref, exprdup(P.alloc, ex)}}; + + case !exptr and metptr; + if !islvalue(&ex) { + err(P, tok.loc, "cannot call `->%s' by reference, lhs is not an lvalue", name); + } else if ty.konst and !recv.konst { + err(P, tok.loc, "constness mismatch: method takes %t but got %t", recv0, ex.ty); + } + ex = { ex.loc, mkptrtype(ty), :UnOp{:addrof, exprdup(P.alloc, ex)}}; + + case exptr and metptr; + if ty.konst and !recv.konst { + err(P, tok.loc, "constness mismatch: method takes %t but got %t", recv0, ex.ty); + } + } + args->push(ex); + let param = &Fn.params[1]; + while !lexmatch(P, #null, ')') { + P.targty = *param++; + args->push(parseexpr(P)); + if args.len > Fn.params.#len and !Fn.variadic { + fatal(P, args->last().loc, "too many args (%z, expected %z)", args.len, Fn.params.#len); } - ex = { ex.loc, mkptrtype(ty), :UnOp{:addrof, exprdup(P.alloc, ex)}}; - - case exptr and metptr; - if ty.konst and !recv.konst { - err(P, tok.loc, "constness mismatch: method takes %t but got %t", recv0, ex.ty); + if !lexmatch(P, #null, ',') { + lexexpect(P, ')'); + break; } - } - args->push(ex); - let param = &Fn.params[1]; - while !lexmatch(P, #null, ')') { - P.targty = *param++; - args->push(parseexpr(P)); - if args.len > Fn.params.#len and !Fn.variadic { - fatal(P, args->last().loc, "too many args (%z, expected %z)", args.len, Fn.params.#len); } - if !lexmatch(P, #null, ',') { - lexexpect(P, ')'); - break; + if args.len < Fn.params.#len { + err(P, ex.loc, "too few args (%z, expected %z)", args.len, Fn.params.#len); } + let met Expr = { tok.loc, f.ty, :Symbol(decl) }; + ex = { tok.loc, .ty: Fn.ret, .u: :Call { exprdup(P.alloc, met), args->move(P.alloc) }}; + + case else; + fatal(P, tok.loc, "cannot call `->%s': not a function or macro", name); } - if args.len < Fn.params.#len { - err(P, ex.loc, "too few args (%z, expected %z)", args.len, Fn.params.#len); + + case VaList; + if !islvalue(&ex) { + err(P, ex.loc, "variadic list receiver must be lvalue"); + } + if exptr { + ex = { ex.loc, ty, :UnOp{:deref, exprdup(P.alloc, ex)}}; } - let met Expr = { tok.loc, f.ty, :Symbol(decl) }; - ex = { tok.loc, .ty: Fn.ret, .u: :Call { exprdup(P.alloc, met), args->move(P.alloc) }}; + switch { + case streq(name, "start"); + lexexpect(P, '('); + if !lexmatch(P, #null, ')') { + let _ = parseexpr(P); + lexmatch(P, #null, ','); + warn(P, ex.loc, "variadic list `start' doesn't need an argument"); + lexexpect(P, ')'); + } + ex = { tok.loc, ty_void, :VaStart(exprdup(P.alloc, ex)) }; - case else; - fatal(P, tok.loc, "cannot call `->%s': not a function or macro", name); + case streq(name, "arg"); + lexexpect(P, '('); + let type = parsetype(P); + lexmatch(P, #null, ','); + lexexpect(P, ')'); + ex = { tok.loc, type, :VaArg(exprdup(P.alloc, ex)) }; + + case streq(name, "end"); + lexexpect(P, '('); + lexmatch(P, #null, ','); + lexexpect(P, ')'); + ex = { tok.loc, ty_void, :VaEnd(exprdup(P.alloc, ex)) }; + + case else + fatal(P, tok.loc, "`%s' is not a valid method for variadic list", name); + } + + case else + fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty); } case else; @@ -2241,28 +2261,30 @@ fn pstwhile(P *Parser, loc Loc, label *const u8) Stmt { } lexexpect(P, '{'); let body Block #?; - with_tmpchange(P.curloop, ++P.loopid) { + let loopid = ++P.loopid; + with_tmpchange(P.curloop, loopid) { let env = mkenv(P.curenv, P.alloc); defer envfree(env); pushenv(P, env); if label { - putdecl(P, loc, { label, loc, .u: :Label(P.loopid) }); + putdecl(P, loc, { label, loc, .u: :Label(loopid) }); } body = parseblock(P); popenv(P); } - return { loc, :While { test, body, P.loopid }}; + return { loc, :While { test, body, loopid }}; } fn pstdowhile(P *Parser, loc Loc, label *const u8) Stmt { lexexpect(P, '{'); let body Block #?; - with_tmpchange(P.curloop, ++P.loopid) { + let loopid = ++P.loopid; + with_tmpchange(P.curloop, loopid) { let env = mkenv(P.curenv, P.alloc); defer envfree(env); pushenv(P, env); if label { - putdecl(P, loc, { label, loc, .u: :Label(P.loopid) }); + putdecl(P, loc, { label, loc, .u: :Label(loopid) }); } body = parseblock(P); popenv(P); @@ -2272,7 +2294,7 @@ fn pstdowhile(P *Parser, loc Loc, label *const u8) Stmt { if !test.ty->is(:Bool) and !test.ty->is(:Ptr) { err(P, test.loc, "do-while condition must be bool or pointer (%t)", test.ty); } - return { loc, :DoWhile { test, body, P.loopid }}; + return { loc, :DoWhile { test, body, loopid }}; } fn pstfor(P *Parser, loc Loc, label *const u8) Stmt { @@ -2311,18 +2333,18 @@ fn pstfor(P *Parser, loc Loc, label *const u8) Stmt { lexexpect(P, '{'); } let body Block #?; - let id = ++P.loopid; - with_tmpchange(P.curloop, id) { + let loopid = ++P.loopid; + with_tmpchange(P.curloop, loopid) { let env = mkenv(P.curenv, P.alloc); defer envfree(env); pushenv(P, env); if label { - putdecl(P, loc, { label, loc, .u: :Label(id) }); + putdecl(P, loc, { label, loc, .u: :Label(loopid) }); } body = parseblock(P); popenv(P); } - return { loc, :For { ini, test, next, body, id }}; + return { loc, :For { ini, test, next, body, loopid }}; } fn pstiswitch(P *Parser, loc Loc, ex Expr) Stmt { @@ -2755,10 +2777,10 @@ fn parsefn(P *Parser, loc Loc, toplevel bool, externp bool, name *const u8) *Dec return decl; } -fn parse4import(P *Parser) [#]Decl; -fn doimport(P *Parser, loc Loc, path *const u8) [#]Decl { +fn parse4import(P *Parser) [#]*Decl; +fn doimport(P *Parser, loc Loc, path *const u8) [#]*Decl { struct Entry { - decls [#]Decl, + decls [#]*Decl, wip bool, } static seen Map<*const u8, Entry, struct { @@ -2993,7 +3015,7 @@ fn parsedecls(P *Parser, loc Loc, yield DeclYielder, yarg *void, toplevel bool) let path = (tok = lexexpects(P, :str, "import path")).u.str.#ptr; let decls = doimport(P, tok.loc, path); foreach(decl, _, decls) { - let decl = putdecl(P, decl.loc, decl); + let decl = putdecl(P, decl.loc, *decl); if yield { yield(decl, yarg); } @@ -3131,14 +3153,14 @@ fn parsedecls(P *Parser, loc Loc, yield DeclYielder, yarg *void, toplevel bool) } } -fn parse4import(P *Parser) [#]Decl { - let decls Vec<Decl> = {}; +fn parse4import(P *Parser) [#]*Decl { + let decls Vec<*Decl> = {}; P.curenv = mkenv(#null, P.alloc); while !P.eof { fn yield(decl *Decl, arg *void) void { - let decls *Vec<Decl> = arg; - decls->push(*decl); + let decls *Vec<*Decl> = arg; + decls->push(decl); } parsedecls(P, P.tokloc, &yield, &decls, #{toplevel} #t); if lexmatch(P, #null, :eof) { |