diff --git a/src/hexer/expander.nim b/src/hexer/expander.nim index 271a2f1a..16f77fa7 100644 --- a/src/hexer/expander.nim +++ b/src/hexer/expander.nim @@ -814,7 +814,7 @@ proc traverseExpr(e: var EContext; c: var Cursor) = inc nested of TupleConstrX: traverseTupleConstr e, c - of CmdX, CallStrLitX, InfixX, PrefixX: + of CmdX, CallStrLitX, InfixX, PrefixX, HcallX: e.dest.add tagToken("call", c.info) inc c inc nested diff --git a/src/lib/nifindexes.nim b/src/lib/nifindexes.nim index ed89a00b..fc5697cb 100644 --- a/src/lib/nifindexes.nim +++ b/src/lib/nifindexes.nim @@ -99,9 +99,41 @@ proc processForChecksum(dest: var Sha1State; content: var TokenBuf) = else: inc n -proc createIndex*(infile: string; buildChecksum: bool; - hookIndexMap: Table[string, seq[(SymId, SymId)]]; - toBuild: TokenBuf) = +type IndexSections* = object + hooks*: Table[string, seq[(SymId, SymId)]] + converters*: seq[(SymId, SymId)] + toBuild*: TokenBuf + +proc getSection(tag: TagId; values: seq[(SymId, SymId)]; symToOffsetMap: Table[SymId, int]): TokenBuf = + let KvT = registerTag "kv" + + result = default(TokenBuf) + result.addParLe tag + + for value in values: + let (key, sym) = value + let offset = symToOffsetMap[sym] + result.buildTree KvT, NoLineInfo: + result.add symToken(key, NoLineInfo) + result.add intToken(pool.integers.getOrIncl(offset), NoLineInfo) + + result.addParRi() + +proc getSymbolSection(tag: TagId; values: seq[(SymId, SymId)]): TokenBuf = + let KvT = registerTag "kv" + + result = default(TokenBuf) + result.addParLe tag + + for value in values: + let (key, sym) = value + result.buildTree KvT, NoLineInfo: + result.add symToken(key, NoLineInfo) + result.add symToken(sym, NoLineInfo) + + result.addParRi() + +proc createIndex*(infile: string; buildChecksum: bool; sections: IndexSections) = let PublicT = registerTag "public" let PrivateT = registerTag "private" let KvT = registerTag "kv" @@ -161,27 +193,24 @@ proc createIndex*(infile: string; buildChecksum: bool; content.add toString(private) content.add "\n" - for (key, values) in hookIndexMap.pairs: - var hookSectionBuf = default(TokenBuf) + for (key, values) in sections.hooks.pairs: let tag = registerTag(key) - hookSectionBuf.addParLe tag - - for value in values: - let (obj, sym) = value - let offset = symToOffsetMap[sym] - hookSectionBuf.buildTree KvT, NoLineInfo: - hookSectionBuf.add symToken(obj, NoLineInfo) - hookSectionBuf.add intToken(pool.integers.getOrIncl(offset), NoLineInfo) - - hookSectionBuf.addParRi() + let hookSectionBuf = getSection(tag, values, symToOffsetMap) content.add toString(hookSectionBuf) content.add "\n" + + if sections.converters.len != 0: + let ConverterT = registerTag "converter" + let converterSectionBuf = getSymbolSection(ConverterT, sections.converters) + + content.add toString(converterSectionBuf) + content.add "\n" let buildT = registerTag "build" var buildBuf = createTokenBuf() buildBuf.addParLe buildT - buildBuf.add toBuild + buildBuf.add sections.toBuild buildBuf.addParRi content.add toString(buildBuf) content.add "\n" @@ -198,7 +227,7 @@ proc createIndex*(infile: string; buildChecksum: bool; writeFile(indexName, content) proc createIndex*(infile: string; buildChecksum: bool) = - createIndex(infile, buildChecksum, initTable[string, seq[(SymId, SymId)]](), default(TokenBuf)) + createIndex(infile, buildChecksum, IndexSections(hooks: initTable[string, seq[(SymId, SymId)]](), toBuild: default(TokenBuf))) type NifIndexEntry* = object @@ -207,6 +236,7 @@ type NifIndex* = object public*, private*: Table[string, NifIndexEntry] hooks*: Table[string, Table[string, NifIndexEntry]] + converters*: Table[string, string] # map of dest types to converter symbols toBuild*: seq[(string, string, string)] proc readSection(s: var Stream; tab: var Table[string, NifIndexEntry]; useAbsoluteOffset = false) = @@ -252,6 +282,49 @@ proc readSection(s: var Stream; tab: var Table[string, NifIndexEntry]; useAbsolu assert false, "expected (kv) construct" #t = next(s) +proc readSymbolSection(s: var Stream; tab: var Table[string, string]) = + let KvT = registerTag "kv" + var t = next(s) + var nested = 1 + while t.kind != EofToken: + let info = t.info + if t.kind == ParLe: + inc nested + if t.tagId == KvT: + t = next(s) + var key: string + if t.kind == Symbol: + key = pool.syms[t.symId] + elif t.kind == Ident: + key = pool.strings[t.litId] + else: + raiseAssert "invalid (kv) construct: symbol expected" + t = next(s) # skip Symbol + var value: string + if t.kind == Symbol: + value = pool.syms[t.symId] + elif t.kind == Ident: + value = pool.strings[t.litId] + else: + raiseAssert "invalid (kv) construct: symbol expected" + t = next(s) # skip value symbol + tab[key] = value + if t.kind == ParRi: + t = next(s) + dec nested + else: + assert false, "invalid (kv) construct: ')' expected" + else: + assert false, "expected (kv) construct" + elif t.kind == ParRi: + dec nested + if nested == 0: + break + t = next(s) + else: + assert false, "expected (kv) construct" + #t = next(s) + proc readIndex*(indexName: string): NifIndex = var s = nifstreams.open(indexName) let res = processDirectives(s.r) @@ -269,6 +342,8 @@ proc readIndex*(indexName: string): NifIndex = let hookSet = toHashSet([ClonerT, TracerT, DisarmerT, MoverT, DtorT]) + let ConverterT = registerTag "converter" + result = default(NifIndex) var t = next(s) if t.tag == IndexT: @@ -288,6 +363,9 @@ proc readIndex*(indexName: string): NifIndex = result.hooks[tagName] = initTable[string, NifIndexEntry]() readSection(s, result.hooks[tagName]) t = next(s) + if t.tag == ConverterT: + readSymbolSection(s, result.converters) + t = next(s) let BuildT = registerTag "build" if t.tag == BuildT: diff --git a/src/nimony/nimony_model.nim b/src/nimony/nimony_model.nim index 4a67087b..944f75e6 100644 --- a/src/nimony/nimony_model.nim +++ b/src/nimony/nimony_model.nim @@ -132,6 +132,7 @@ type CallStrLitX = "callstrlit" InfixX = "infix" PrefixX = "prefix" + HcallX = "hcall" # hidden converter call CmdX = "cmd" InfX = "inf" NegInfX = "neginf" @@ -334,7 +335,7 @@ template `==`*(n: Cursor; s: string): bool = n.kind == ParLe and pool.tags[n.tag const RoutineKinds* = {ProcY, FuncY, IterY, TemplateY, MacroY, ConverterY, MethodY} - CallKinds* = {CallX, CallStrLitX, CmdX, PrefixX, InfixX} + CallKinds* = {CallX, CallStrLitX, CmdX, PrefixX, InfixX, HcallX} ConvKinds* = {HconvX, ConvX, OconvX, DconvX, CastX} proc addParLe*(dest: var TokenBuf; kind: TypeKind|SymKind|ExprKind|StmtKind|SubstructureKind; info = NoLineInfo) = diff --git a/src/nimony/programs.nim b/src/nimony/programs.nim index daef9c0d..843c9ae6 100644 --- a/src/nimony/programs.nim +++ b/src/nimony/programs.nim @@ -49,6 +49,7 @@ proc load*(suffix: string): NifModule = proc loadInterface*(suffix: string; iface: var Iface; module: SymId; importTab: var OrderedTable[StrId, seq[SymId]]; + converters: var Table[SymId, seq[SymId]]; marker: var PackedSet[StrId]; negateMarker: bool) = let m = load(suffix) for k, _ in m.index.public: @@ -63,6 +64,15 @@ proc loadInterface*(suffix: string; iface: var Iface; if not symMarked: # mark that this module contains the identifier `strId`: importTab.mgetOrPut(strId, @[]).add(module) + for k, v in m.index.converters: + var name = v + extractBasename(name) + let nameId = pool.strings.getOrIncl(name) + # check that the converter is imported, slow but better to be slow here: + if nameId in importTab and module in importTab[nameId]: + let key = pool.syms.getOrIncl(k) + let val = pool.syms.getOrIncl(v) + converters.mgetOrPut(key, @[]).add(val) proc error*(msg: string; c: Cursor) {.noreturn.} = when defined(debug): diff --git a/src/nimony/sem.nim b/src/nimony/sem.nim index 7a9e829e..ee5f9591 100644 --- a/src/nimony/sem.nim +++ b/src/nimony/sem.nim @@ -146,6 +146,12 @@ proc implicitlyDiscardable(n: Cursor, noreturnOnly = false): bool = proc isNoReturn(n: Cursor): bool {.inline.} = result = implicitlyDiscardable(n, noreturnOnly = true) +proc addArgsInstConverters(c: var SemContext; m: var Match; origArgs: openArray[Item]) + +template emptyNode(): Cursor = + # XXX find a better solution for this + c.types.voidType + proc commonType(c: var SemContext; it: var Item; argBegin: int; expected: TypeCursor) = if typeKind(expected) == AutoT: return @@ -154,7 +160,7 @@ proc commonType(c: var SemContext; it: var Item; argBegin: int; expected: TypeCu return let info = it.n.info - var m = createMatch(addr c) + var m = createMatch(addr c, matchConverters = true) var arg = Item(n: cursorAt(c.dest, argBegin), typ: it.typ) if typeKind(arg.typ) == VoidT and isNoReturn(arg.n): # noreturn allowed in expression context @@ -167,7 +173,7 @@ proc commonType(c: var SemContext; it: var Item; argBegin: int; expected: TypeCu c.typeMismatch info, it.typ, expected else: shrink c.dest, argBegin - c.dest.add m.args + addArgsInstConverters(c, m, [Item(n: emptyNode(), typ: arg.typ)]) it.typ = expected proc producesVoid(c: var SemContext; info: PackedLineInfo; dest: var Cursor) = @@ -249,7 +255,7 @@ proc importSingleFile(c: var SemContext; f1: ImportedFilename; origin: string; m publish moduleSym, moduleDecl var module = ImportedModule() var marker = mode.list - loadInterface suffix, module.iface, moduleSym, c.importTab, + loadInterface suffix, module.iface, moduleSym, c.importTab, c.converters, marker, negateMarker = mode.kind == FromImport c.importedModules[moduleSym] = module @@ -518,6 +524,16 @@ proc instantiateGenericType(c: var SemContext; req: InstRequest) = var n = beginRead(dest) semTypeSection c, n +proc instantiateType(c: var SemContext; typ: Cursor; bindings: Table[SymId, Cursor]): Cursor = + var dest = createTokenBuf(30) + var sc = SubsContext(params: addr bindings) + subs(c, dest, sc, typ) + var sub = beginRead(dest) + var instDest = createTokenBuf(30) + swap c.dest, instDest + result = semLocalType(c, sub) + swap c.dest, instDest + type PassKind = enum checkSignatures, checkBody, checkGenericInst, checkConceptProc @@ -712,10 +728,6 @@ proc semStmt(c: var SemContext; n: var Cursor; isNewScope: bool) = buildErr c, info, "expression of type `" & typeToString(it.typ) & "` must be discarded" n = it.n -template emptyNode(): Cursor = - # XXX find a better solution for this - c.types.voidType - template skipToLocalType(n) = inc n # skip ParLe inc n # skip name @@ -882,7 +894,7 @@ proc maybeAddConceptMethods(c: var SemContext; fn: StrId; typevar: SymId; cands: proc considerTypeboundOps(c: var SemContext; m: var seq[Match]; candidates: FnCandidates; args: openArray[Item], genericArgs: Cursor) = for candidate in candidates.a: - m.add createMatch(addr c) + m.add createMatch(addr c, matchConverters = true) sigmatch(m[^1], candidate, args, genericArgs) proc requestRoutineInstance(c: var SemContext; origin: SymId; @@ -947,24 +959,6 @@ proc getFnIdent(c: var SemContext): StrId = result = getIdent(n) endRead(c.dest) -proc containsGenericParams(n: TypeCursor): bool = - var n = n - var nested = 0 - while true: - case n.kind - of Symbol: - let res = tryLoadSym(n.symId) - if res.status == LacksNothing and res.decl == $TypevarY: - return true - of ParLe: - inc nested - of ParRi: - dec nested - else: discard - if nested == 0: break - inc n - return false - type DotExprState = enum MatchedDotField ## matched a dot field, i.e. result is a dot expression @@ -1159,6 +1153,45 @@ proc buildCallSource(buf: var TokenBuf; cs: CallState) = proc semReturnType(c: var SemContext; n: var Cursor): TypeCursor = result = semLocalType(c, n, InReturnTypeDecl) +proc addArgsInstConverters(c: var SemContext; m: var Match; origArgs: openArray[Item]) = + if not m.genericConverter: + c.dest.add m.args + else: + m.args.addParRi() + var arg = beginRead(m.args) + var i = 0 + while arg.kind != ParRi: + var nested = 0 + while arg.exprKind in {HconvX, OconvX, HderefX, HaddrX}: + takeToken c, arg + inc nested + if arg.exprKind == HcallX: + let convInfo = arg.info + takeToken c, arg + inc nested + if arg.kind == Symbol: + let sym = arg.symId + takeToken c, arg + let res = tryLoadSym(sym) + if res.status == LacksNothing and res.decl.symKind == ConverterY: + let routine = asRoutine(res.decl) + if isGeneric(routine): + let conv = FnCandidate(kind: routine.kind, sym: sym, typ: routine.params) + var convMatch = createMatch(addr c, matchConverters = false) + sigmatch convMatch, conv, [Item(n: arg, typ: origArgs[i].typ)], emptyNode() + # ^ could also use origArgs[i] directly but commonType would have to keep the expression alive + assert not convMatch.err + let inst = c.requestRoutineInstance(conv.sym, convMatch.typeArgs, convMatch.inferred, convInfo) + c.dest[c.dest.len-1].setSymId inst.targetSym + while true: + case arg.kind + of ParLe: inc nested + of ParRi: dec nested + else: discard + takeToken c, arg + if nested == 0: break + inc i + proc resolveOverloads(c: var SemContext; it: var Item; cs: var CallState) = let genericArgs = if cs.hasGenericArgs: cursorAt(cs.genericDest, 0) @@ -1173,7 +1206,7 @@ proc resolveOverloads(c: var SemContext; it: var Item; cs: var CallState) = let sym = f.symId let s = fetchSym(c, sym) let candidate = FnCandidate(kind: s.kind, sym: sym, typ: fetchType(c, f, s)) - m.add createMatch(addr c) + m.add createMatch(addr c, matchConverters = true) sigmatch(m[^1], candidate, cs.args, genericArgs) else: buildErr c, cs.fn.n.info, "`choice` node does not contain `symbol`" @@ -1193,7 +1226,7 @@ proc resolveOverloads(c: var SemContext; it: var Item; cs: var CallState) = # Keep in mind that proc vars are a thing: let sym = if cs.fn.n.kind == Symbol: cs.fn.n.symId else: SymId(0) let candidate = FnCandidate(kind: cs.fnKind, sym: sym, typ: cs.fn.typ) - m.add createMatch(addr c) + m.add createMatch(addr c, matchConverters = true) sigmatch(m[^1], candidate, cs.args, genericArgs) considerTypeboundOps(c, m, cs.candidates, cs.args, genericArgs) let idx = pickBestMatch(c, m) @@ -1202,7 +1235,7 @@ proc resolveOverloads(c: var SemContext; it: var Item; cs: var CallState) = c.dest.add cs.callNode let finalFn = m[idx].fn let isMagic = c.addFn(finalFn, cs.fn.n, cs.args) - c.dest.add m[idx].args + addArgsInstConverters(c, m[idx], cs.args) wantParRi c, it.n if finalFn.kind == TemplateY: @@ -2875,6 +2908,15 @@ proc semProc(c: var SemContext; it: var Item; kind: SymKind; pass: PassKind) = skip it.n publishSignature c, symId, declStart + if kind == ConverterY: + let root = nominalRoot(c.routine.returnType) + if root == SymId(0): + buildErr c, info, "cannot attach converter to type " & typeToString(c.routine.returnType) + else: + c.converters.mgetOrPut(root, @[]).add(symId) + if pass == checkBody and c.dest[beforeExportMarker].kind != DotToken: + # don't register instances + c.converterIndexMap.add((root, symId)) if it.n.kind != DotToken: case pass of checkGenericInst: @@ -4073,7 +4115,8 @@ proc tryExplicitRoutineInst(c: var SemContext; syms: Cursor; it: var Item): bool let sym = syms.symId let routine = getProcDecl(sym) let candidate = FnCandidate(kind: routine.kind, sym: sym, typ: routine.params) - var m = Match(fn: candidate) + var m = createMatch(addr c) + m.fn = candidate matchTypevars m, candidate, args if not m.err: # match @@ -4739,7 +4782,7 @@ proc semExpr(c: var SemContext; it: var Item; flags: set[SemFlag] = {}) = inc it.n semExpr c, it skipParRi it.n - of CallX, CmdX, CallStrLitX, InfixX, PrefixX: + of CallX, CmdX, CallStrLitX, InfixX, PrefixX, HcallX: toplevelGuard c: semCall c, it of DotX, DerefDotX: @@ -4858,7 +4901,7 @@ proc writeOutput(c: var SemContext; outfile: string) = #b.addRaw toString(c.dest) #b.close() writeFile outfile, "(.nif24)\n" & toString(c.dest) - createIndex outfile, true, c.hookIndexMap, c.toBuild + createIndex outfile, true, IndexSections(hooks: c.hookIndexMap, converters: c.converterIndexMap, toBuild: c.toBuild) proc phaseX(c: var SemContext; n: Cursor; x: SemPhase): TokenBuf = assert n == "stmts" @@ -4919,7 +4962,8 @@ proc semcheck*(infile, outfile: string; config: sink NifConfig; moduleFlags: set phase: SemcheckTopLevelSyms, routine: SemRoutine(kind: NoSym), commandLineArgs: commandLineArgs, - canSelfExec: canSelfExec) + canSelfExec: canSelfExec, + instantiateType: instantiateType) c.currentScope = Scope(tab: initTable[StrId, seq[Sym]](), up: nil, kind: ToplevelScope) # XXX could add self module symbol here diff --git a/src/nimony/semdata.nim b/src/nimony/semdata.nim index 24d56ff3..469d1926 100644 --- a/src/nimony/semdata.nim +++ b/src/nimony/semdata.nim @@ -96,5 +96,8 @@ type meta*: MetaInfo genericHooks*: Table[SymId, seq[SymId]] hookIndexMap*: Table[string, seq[(SymId, SymId)]] + converters*: Table[SymId, seq[SymId]] + converterIndexMap*: seq[(SymId, SymId)] + instantiateType*: proc (c: var SemContext; typ: Cursor; bindings: Table[SymId, Cursor]): Cursor freshSyms*: HashSet[SymId] ## symdefs that should count as new for semchecking toBuild*: TokenBuf diff --git a/src/nimony/sigmatch.nim b/src/nimony/sigmatch.nim index a52260dd..672e69e3 100644 --- a/src/nimony/sigmatch.nim +++ b/src/nimony/sigmatch.nim @@ -39,8 +39,11 @@ type context: ptr SemContext error: MatchError firstVarargPosition*: int + matchConverters*: bool + genericConverter*: bool -proc createMatch*(context: ptr SemContext): Match = Match(context: context, firstVarargPosition: -1) +proc createMatch*(context: ptr SemContext; matchConverters = false): Match = + Match(context: context, matchConverters: matchConverters, firstVarargPosition: -1) proc concat(a: varargs[string]): string = result = a[0] @@ -634,8 +637,66 @@ proc singleArgImpl(m: var Match; f: var Cursor; arg: Item) = else: m.error "BUG: " & expected(f, arg.typ) +proc tryConverter(m: var Match; conv: SymId; f: Cursor; arg: Item) = + let res = tryLoadSym(conv) + assert res.status == LacksNothing + var fn = asRoutine(res.decl) + assert fn.kind == ConverterY + var src = fn.params + inc src + src = asLocal(src).typ + let srcStart = m.args.len + let srcCost = m.intCosts + let startBindings = m.inferred + singleArgImpl(m, src, arg) + if m.err: return + if srcCost != m.intCosts: + # cannot be conversion match + m.err = true + return + var dest = fn.retType + if m.inferred.len != 0 and containsGenericParams(dest): + dest = m.context.instantiateType(m.context[], dest, m.inferred) + m.inferred = startBindings + var callBuf = createTokenBuf(16) # dummy call node to use for matching dest type + let callTokens = [ + parLeToken(HcallX, arg.n.info), + symToken(conv, arg.n.info) + ] + for tok in callTokens: callBuf.add tok + callBuf.addSubtree arg.n + callBuf.addParRi() + let destCosts = (m.intCosts, m.inheritanceCosts) + var destArg = Item(n: cursorAt(callBuf, 0), typ: dest) + var fMatch = f + singleArgImpl(m, fMatch, destArg) + if m.err: return + if destCosts != (m.intCosts, m.inheritanceCosts): + # cannot be subtype or conversion match + m.err = true + return + m.args.insert callTokens, srcStart + inc m.opened + if isGeneric(fn): m.genericConverter = true + proc singleArg(m: var Match; f: var Cursor; arg: Item) = + let fOrig = f singleArgImpl(m, f, arg) + if m.err and m.matchConverters: + # try converter + let root = nominalRoot(fOrig) + if root != SymId(0): + let converters = m.context.converters.getOrDefault(root) + if converters.len != 0: + let oldErr = m.error + for conv in converters: + m.err = false + tryConverter(m, conv, fOrig, arg) + if not m.err: + # converter matched + break + if m.err: + m.error = oldErr if not m.err: m.useArg arg # since it was a match, copy it while m.opened > 0: diff --git a/src/nimony/typenav.nim b/src/nimony/typenav.nim index 243c0c39..ad5a64e9 100644 --- a/src/nimony/typenav.nim +++ b/src/nimony/typenav.nim @@ -128,7 +128,7 @@ proc getTypeImpl(c: var TypeCache; n: Cursor): Cursor = skip n if n.kind == ParRi: result = getTypeImpl(c, prev) - of CallX, CallStrLitX, InfixX, PrefixX, CmdX: + of CallX, CallStrLitX, InfixX, PrefixX, CmdX, HcallX: result = getTypeImpl(c, n.firstSon) if isRoutine(symKind(result)): let routine = asRoutine(result) diff --git a/src/nimony/typeprops.nim b/src/nimony/typeprops.nim index dae0c789..c3efb057 100644 --- a/src/nimony/typeprops.nim +++ b/src/nimony/typeprops.nim @@ -1,3 +1,4 @@ +import std/assertions include nifprelude import nimony_model, decls, xints, semdata, programs, nifconfig @@ -200,3 +201,52 @@ proc lengthOrd*(c: var SemContext; typ: TypeCursor): xint = let last = lastOrd(c, typ) if last.isNaN: return last result = last - first + createXint(1.uint64) + +proc containsGenericParams*(n: TypeCursor): bool = + var n = n + var nested = 0 + while true: + case n.kind + of Symbol: + let res = tryLoadSym(n.symId) + if res.status == LacksNothing and res.decl == $TypevarY: + return true + of ParLe: + inc nested + of ParRi: + dec nested + else: discard + if nested == 0: break + inc n + return false + +proc nominalRoot*(t: TypeCursor): SymId = + result = SymId(0) + var t = t + while true: + case t.kind + of Symbol: + let res = tryLoadSym(t.symId) + assert res.status == LacksNothing + if res.decl.symKind == TypeY: + let decl = asTypeDecl(res.decl) + if decl.typevars.typeKind == InvokeT: + var root = decl.typevars + inc root + assert root.kind == Symbol + return root.symId + else: + return t.symId + else: + # includes typevar case + break + of ParLe: + case t.typeKind + of MutT, OutT, LentT, SinkT, StaticT, TypedescT: + inc t + of InvokeT: + inc t + else: + break + else: + break diff --git a/tests/nimony/sysbasics/tconverter.nim b/tests/nimony/sysbasics/tconverter.nim new file mode 100644 index 00000000..370e0ece --- /dev/null +++ b/tests/nimony/sysbasics/tconverter.nim @@ -0,0 +1,9 @@ +type Foo = object + val: int + +converter toFoo(x: int): Foo = Foo(val: x) + +proc bar(x: Foo) = discard + +bar(123) +let foo: Foo = 123 diff --git a/tests/nimony/sysbasics/tgenericconverter.nim b/tests/nimony/sysbasics/tgenericconverter.nim new file mode 100644 index 00000000..0abeb806 --- /dev/null +++ b/tests/nimony/sysbasics/tgenericconverter.nim @@ -0,0 +1,9 @@ +type Foo[T] = object + val: T + +converter toFoo[T](x: T): Foo[T] = Foo[T](val: x) + +proc bar[T](x: Foo[T], y: T) = discard + +bar(123, 456) +let foo: Foo[int] = 123