Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lang: add basic unpacking for union types #110

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions languages/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ expr ::= <ident>
| (Exprs <expr>+)
| (Asgn <expr> <expr>)
| (Decl <ident> <expr>)
| (Match <expr> (As <ident> <texpr> <expr>)+)
texpr ::= <ident>
| (VoidTy)
| (UnitTy)
Expand Down
76 changes: 76 additions & 0 deletions passes/source2il.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,82 @@ proc exprToIL(c; t: InTree, n: NodeIndex, expr, stmts): ExprType =
of SourceKind.Unreachable:
stmts.add newUnreachable()
result = prim(tkVoid) + {}
of SourceKind.Match:
var e = c.exprToIL(t, t.child(n, 0))
if e.typ.kind notin {tkUnion, tkError}:
c.error("expression must be of union type")
e.typ = errorType()

var
got = newSeq[SemType]() ## the handled types
handlers = newSeq[tuple[local: IrNode, body: Expr]]()
res = prim(tkVoid) # start with the bottom type

# the type of the Match expression is the least upper bound between all
# handlers. Thus, analysis has to happen in two passes: the first one
# analyses all handlers, and the second one does the fitting and generates
# the dispatcher
for it in t.items(n, 1):
let
(nameNode, typNode, bodyNode) = t.triplet(it)
typ = c.expectNot(c.evalType(t, typNode), tkVoid)
at = lowerBound(got, typ, cmp)

c.openScope()
let name {.cursor.} = t.getString(nameNode)
if c.lookup(name).kind != ekNone:
c.error("redeclaration of '$1'" % [name])
let tmp = c.newTemp(typ)
c.addDecl(name, Entity(kind: ekLocal, id: tmp.id.int))
let body = c.exprToIL(t, bodyNode)
c.closeScope()

let newResult = commonType(res, body.typ)
if tkError notin {res.kind, body.typ.kind} and
newResult.kind == tkError:
c.error("'$1' and '$2' cannot be unified into a single type" %
[$res, $body.typ])
res = newResult

if at >= got.len or got[at] != typ:
got.insert typ, at
handlers.add (tmp, body)
else:
c.error("duplicate handler for '$1'" % [$typ])

expr = c.newTemp(res)

# second pass:
if e.typ.kind == tkUnion:
let target = capture(c, e, stmts) ## the union to match against
var handled: int
# XXX: the IL require the selector expression for a case statement to
# be a simple expression (which a field expression is not), hence
# the intermediate temporary
let sel = c.newTemp(prim(tkInt))
stmts.add newAsgn(sel, newFieldExpr(target, 0))
var caseStmt = newCase(c.typeToIL(prim(tkInt)), sel)
for i, it in handlers.pairs:
let idx = e.typ.elems.find(c.locals[it.local.id])
if idx != -1:
var body = IrNode(kind: Stmts)
body.add newAsgn(it.local, newFieldExpr(newFieldExpr(target, 1), idx))
body.add wrap(c, c.fitExpr(it.body, res), expr).children
caseStmt.add newChoice(newIntVal(idx), body)
inc handled
else:
c.error("'$1' is not part of '$2'" %
[$c.locals[it.local.id], $e.typ])

if handled != e.typ.elems.len:
# not all types in the union have a matching handler
for it in e.typ.elems.items:
if find(got, it) == -1:
c.error("a matcher for '$1' is missing" % [$it])

stmts.add caseStmt

result = res + {}
of SourceKind.Exprs:
let last = t.len(n) - 1
var seenVoidExpr = false
Expand Down
4 changes: 3 additions & 1 deletion passes/syntax_source.nim
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type
Asgn
Return
Unreachable
Match, As
Params
ProcDecl, ParamDecl
Decl
Expand All @@ -34,7 +35,8 @@ type

const
ExprNodes* = {IntVal, FloatVal, Ident, And, Or, If, While, Call, TupleCons,
Seq, FieldAccess, At, Asgn, Return, Unreachable, Exprs, Decl}
Seq, FieldAccess, At, Asgn, Return, Unreachable, Match,
Exprs, Decl}
DeclNodes* = {ProcDecl, TypeDecl}
AllNodes* = {low(NodeKind) .. high(NodeKind)}

Expand Down
Loading