Skip to content

Commit

Permalink
implement standalone explicit routine instantiations (#348)
Browse files Browse the repository at this point in the history
* implement standalone explicit routine instantiations

* no need for finishTypevarMatch with enforced param count

* add closing paren for explicit generic calls, support symchoices
  • Loading branch information
metagn authored Jan 10, 2025
1 parent 5213495 commit d2f9b48
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 13 deletions.
104 changes: 96 additions & 8 deletions src/nimony/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1258,18 +1258,20 @@ proc semCall(c: var SemContext; it: var Item; source: TransformedCallSource = Re
swap c.dest, lhsBuf
cs.fn.n = lhs.n
lhs.n = cursorAt(lhsBuf, 0)
if lhs.n.kind == Symbol and isRoutine(lhs.kind):
let res = tryLoadSym(lhs.n.symId)
var maybeRoutine = lhs.n
if maybeRoutine.exprKind in {OchoiceX, CchoiceX}:
inc maybeRoutine
if maybeRoutine.kind == Symbol:
let res = tryLoadSym(maybeRoutine.symId)
assert res.status == LacksNothing
if isGeneric(asRoutine(res.decl)):
if isRoutine(res.decl.symKind) and isGeneric(asRoutine(res.decl)):
cs.hasGenericArgs = true
cs.genericDest = createTokenBuf(16)
swap c.dest, cs.genericDest
while cs.fn.n.kind != ParRi:
# XXX semLocalType should build `static` types for values
discard semLocalType(c, cs.fn.n)
semLocalTypeImpl c, cs.fn.n, AllowValues
wantParRi c, cs.fn.n
swap c.dest, cs.genericDest
skipParRi cs.fn.n
it.n = cs.fn.n
c.dest.addSubtree lhs.n
cs.fn.typ = lhs.typ
Expand Down Expand Up @@ -3917,16 +3919,102 @@ proc semIsMainModule(c: var SemContext; it: var Item) =
it.typ = c.types.boolType
commonType c, it, beforeExpr, expected

proc tryExplicitRoutineInst(c: var SemContext; syms: Cursor; it: var Item): bool =
result = false
let info = syms.info
let exprStart = c.dest.len
# build symchoice first so we can directly add the matching syms:
c.dest.add parLeToken(AtX, info)
c.dest.add parLeToken(CchoiceX, info)
var argBuf = createTokenBuf(16)
swap c.dest, argBuf
var argRead = it.n
while argRead.kind != ParRi:
semLocalTypeImpl c, argRead, AllowValues
wantParRi c, argRead
swap c.dest, argBuf
let args = cursorAt(argBuf, 0)
var matches = 0
var lastMatch = default(Match)
var instLastMatch = false
var syms = syms
var nested = 0
while true:
# find matching syms
case syms.kind
of ParLe:
if syms.exprKind in {CchoiceX, OchoiceX}:
inc nested
inc syms
else:
c.dest.shrink exprStart
c.buildErr syms.info, "invalid tag in symchoice: " & pool.tags[syms.tagId]
return
of ParRi:
dec nested
inc syms
of Symbol:
let sym = syms.symId
let routine = getProcDecl(sym)
let candidate = FnCandidate(kind: routine.kind, sym: sym, typ: routine.params)
var m = Match(fn: candidate)
matchTypevars m, candidate, args
if not m.err:
# match
c.dest.add symToken(sym, syms.info)
inc matches
lastMatch = m
# mark if routine is suitable for instantiation:
instLastMatch = routine.kind notin {TemplateY, MacroY} and routine.exported.kind != ParLe
inc syms
else:
c.dest.shrink exprStart
c.buildErr syms.info, "invalid token in symchoice: " & $syms.kind
return
if nested == 0: break
c.dest.addParRi() # close symchoice
if matches == 0:
c.dest.shrink exprStart
result = false
elif matches == 1 and c.routine.inGeneric == 0 and instLastMatch:
# can instantiate single match
c.dest.shrink exprStart
# inferred table outlives proc but argsBuf is ephemeral:
var inferred = lastMatch.inferred
for _, value in inferred.mpairs:
c.dest.addSubtree value
value = typeToCursor(c, exprStart)
c.dest.shrink exprStart
let inst = c.requestRoutineInstance(lastMatch.fn.sym, lastMatch.typeArgs, inferred, info)
c.dest.add symToken(inst.targetSym, info)
it.typ = asRoutine(inst.procType).params
it.kind = lastMatch.fn.kind
it.n = argRead
result = true
else:
# multiple matches, leave as subscript of symchoice
c.dest.add argBuf
it.n = argRead
result = true

proc tryBuiltinSubscript(c: var SemContext; it: var Item; lhs: Item): bool =
# it.n is after lhs, at args
result = false
if lhs.n.kind == Symbol and lhs.kind == TypeY and
getTypeSection(lhs.n.symId).typevars == "typevars":
isGeneric(getTypeSection(lhs.n.symId)):
# lhs is a generic type symbol, this is a generic invocation
# treat it as a type expression to call semInvoke
semLocalTypeExpr c, it
return true
# XXX also check for proc generic instantiation, including symchoice
var maybeRoutine = lhs.n
if maybeRoutine.exprKind in {OchoiceX, CchoiceX}:
inc maybeRoutine
if maybeRoutine.kind == Symbol:
let res = tryLoadSym(maybeRoutine.symId)
if res.status == LacksNothing and isRoutine(res.decl.symKind):
# check for explicit generic routine instantiation
result = tryExplicitRoutineInst(c, lhs.n, it)
if result: return

proc semBuiltinSubscript(c: var SemContext; it: var Item; lhs: Item) =
# it.n is after lhs, at args
Expand Down
19 changes: 14 additions & 5 deletions src/nimony/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -706,16 +706,17 @@ proc collectDefaultValues(f: var Cursor): seq[Item] =
result.add Item(n: param.val, typ: param.typ)
skip f

proc sigmatch*(m: var Match; fn: FnCandidate; args: openArray[Item];
explicitTypeVars: Cursor) =
assert fn.kind != NoSym or fn.sym == SymId(0)
proc matchTypevars*(m: var Match; fn: FnCandidate; explicitTypeVars: Cursor) =
m.tvars = initHashSet[SymId]()
m.fn = fn
if fn.kind in RoutineKinds:
var e = explicitTypeVars
for v in typeVars(fn.sym):
m.tvars.incl v
if e.kind != DotToken and e.kind != ParRi:
if e.kind == DotToken: discard
elif e.kind == ParRi:
m.error "missing explicit generic parameter for " & pool.syms[v]
break
else:
if matchesConstraint(m, v, e):
m.inferred[v] = e
else:
Expand All @@ -725,12 +726,20 @@ proc sigmatch*(m: var Match; fn: FnCandidate; args: openArray[Item];
assert typevar.kind == TypevarY
m.error concat(typeToString(e), " does not match constraint ", typeToString(typevar.typ))
skip e
if e.kind != DotToken and e.kind != ParRi:
m.error "extra generic parameter"
elif explicitTypeVars.kind != DotToken:
# aka there are explicit type vars
if m.tvars.len == 0:
m.error "routine is not generic"
return

proc sigmatch*(m: var Match; fn: FnCandidate; args: openArray[Item];
explicitTypeVars: Cursor) =
assert fn.kind != NoSym or fn.sym == SymId(0)
m.fn = fn
matchTypevars m, fn, explicitTypeVars

var f = fn.typ
assert f == "params"
inc f # "params"
Expand Down
7 changes: 7 additions & 0 deletions tests/nimony/generics/texplicitprocinst.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
proc foo[T](x: T) = discard
proc foo[T, U](x: T, y: U) = discard

discard foo[int]
discard foo[int, string]
foo[int](123)
foo[int, string](123, "abc")

0 comments on commit d2f9b48

Please sign in to comment.