diff --git a/compiler/concepts.nim b/compiler/concepts.nim index d48bacdc5257..800735d3182a 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -11,7 +11,7 @@ ## for details. Note this is a first implementation and only the "Concept matching" ## section has been implemented. -import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types +import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable import std/intsets @@ -309,7 +309,7 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool = # error was reported earlier. result = false -proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool = +proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType): bool = ## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the ## concept's requirements. If so, we return true and fill the 'bindings' with pairs of @@ -328,16 +328,16 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; i dest = existingBinding(m, dest) if dest == nil or dest.kind != tyGenericParam: break if dest != nil: - bindings.idTablePut(a, dest) + bindings.put(a, dest) when logBindings: echo "A bind ", a, " ", dest else: - bindings.idTablePut(a, b) + bindings.put(a, b) when logBindings: echo "B bind ", a, " ", b # we have a match, so bind 'arg' itself to 'concpt': - bindings.idTablePut(concpt, arg) + bindings.put(concpt, arg) # invocation != nil means we have a non-atomic concept: if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1: # bind even more generic parameters assert invocation.kind == tyGenericInvocation for i in FirstGenericParamAt ..< invocation.kidsLen: - bindings.idTablePut(invocation[i], arg[i]) + bindings.put(invocation[i], arg[i]) diff --git a/compiler/layeredtable.nim b/compiler/layeredtable.nim new file mode 100644 index 000000000000..6655911c3851 --- /dev/null +++ b/compiler/layeredtable.nim @@ -0,0 +1,87 @@ +import std/tables +import ast + +type + LayeredIdTableObj* {.acyclic.} = object + topLayer*: TypeMapping + nextLayer*: ref LayeredIdTableObj + previousLen*: int # used to track if bindings were added + +const useRef = not defined(gcDestructors) + +when useRef: + type LayeredIdTable* = ref LayeredIdTableObj +else: + type LayeredIdTable* = LayeredIdTableObj + +proc initLayeredTypeMap*(pt: sink TypeMapping = initTypeMapping()): LayeredIdTable = + result = LayeredIdTable(topLayer: pt, nextLayer: nil) + +proc shallowCopy*(pt: LayeredIdTable): LayeredIdTable {.inline.} = + result = LayeredIdTable(topLayer: pt.topLayer, nextLayer: pt.nextLayer, previousLen: pt.previousLen) + +proc currentLen*(pt: LayeredIdTable): int = + pt.previousLen + pt.topLayer.len + +proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable = + result = LayeredIdTable(topLayer: initTable[ItemId, PType](), previousLen: pt.currentLen) + when useRef: + result.nextLayer = pt + else: + new(result.nextLayer) + result.nextLayer[] = pt + +proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} = + when useRef: + pt = pt.nextLayer + else: + when defined(gcDestructors): + pt = pt.nextLayer[] + else: + # workaround refc + let tmp = pt.nextLayer[] + pt = tmp + +proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType = + result = nil + var tm = typeMap + while tm != nil: + result = getOrDefault(tm.topLayer, key) + if result != nil: return + tm = tm.nextLayer + +template lookup*(typeMap: ref LayeredIdTableObj, key: PType): PType = + lookup(typeMap, key.itemId) + +when not useRef: + proc lookup(typeMap: LayeredIdTableObj, key: ItemId): PType {.inline.} = + result = getOrDefault(typeMap.topLayer, key) + if result == nil and typeMap.nextLayer != nil: + result = lookup(typeMap.nextLayer, key) + + template lookup*(typeMap: LayeredIdTableObj, key: PType): PType = + lookup(typeMap, key.itemId) + +proc put(typeMap: var LayeredIdTable, key: ItemId, value: PType) {.inline.} = + typeMap.topLayer[key] = value + +template put*(typeMap: var LayeredIdTable, key, value: PType) = + put(typeMap, key.itemId, value) + +proc putRecursive(typeMap: ref LayeredIdTableObj, key: ItemId, value: PType) = + var tm = typeMap + while tm != nil: + tm.topLayer[key] = value + tm = tm.nextLayer + +template putRecursive*(typeMap: ref LayeredIdTableObj, key, value: PType) = + putRecursive(typeMap, key.itemId, value) + +when not useRef: + proc putRecursive(typeMap: var LayeredIdTableObj, key: ItemId, value: PType) {.inline.} = + put(typeMap, key, value) + if typeMap.nextLayer != nil: + putRecursive(typeMap.nextLayer, key, value) + + template putRecursive*(typeMap: var LayeredIdTableObj, key, value: PType) = + putRecursive(typeMap, key.itemId, value) diff --git a/compiler/sem.nim b/compiler/sem.nim index 72d8dc5b040a..d8a9e50043d6 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -18,7 +18,7 @@ import evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, lowerings, plugins/active, lineinfos, int128, isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs, - extccomp + extccomp, layeredtable import vtables import std/[strtabs, math, tables, intsets, strutils, packedsets] @@ -473,7 +473,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, # e.g. template foo(T: typedesc): seq[T] # We will instantiate the return type here, because # we now know the supplied arguments - var paramTypes = initTypeMapping() + var paramTypes = initLayeredTypeMap() for param, value in genericParamsInMacroCall(s, call): var givenType = value.typ # the sym nodes used for the supplied generic arguments for @@ -481,7 +481,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, # in this case, get the type directly from the sym if givenType == nil and value.kind == nkSym and value.sym.typ != nil: givenType = value.sym.typ - idTablePut(paramTypes, param.typ, givenType) + put(paramTypes, param.typ, givenType) retType = generateTypeInstance(c, paramTypes, macroResult.info, retType) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 13f2273a9b9d..02c6529ae7c8 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -748,7 +748,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = if t[i] == nil or u[i] == nil: return stackPut(t[i], u[i]) of tyGenericParam: - let prebound = x.bindings.idTableGet(t) + let prebound = x.bindings.lookup(t) if prebound != nil: continue # Skip param, already bound @@ -760,7 +760,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = discard # update bindings for i in 0 ..< flatUnbound.len(): - x.bindings.idTablePut(flatUnbound[i], flatBound[i]) + x.bindings.put(flatUnbound[i], flatBound[i]) proc semResolvedCall(c: PContext, x: var TCandidate, n: PNode, flags: TExprFlags; diff --git a/compiler/semdata.nim b/compiler/semdata.nim index ca35ddc53a6e..16d3d6f5b85b 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -15,8 +15,8 @@ when defined(nimPreviewSlimSystem): import std/assertions import - options, ast, astalgo, msgs, idents, renderer, - magicsys, vmdef, modulegraphs, lineinfos, pathutils + options, ast, msgs, idents, renderer, + magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable import ic / ic @@ -136,10 +136,10 @@ type semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} - semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode - semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType], + semInferredLambda*: proc(c: PContext, pt: LayeredIdTable, n: PNode): PNode + semGenerateInstance*: proc (c: PContext, fn: PSym, pt: LayeredIdTable, info: TLineInfo): PSym - instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping, + instantiateOnlyProcType*: proc (c: PContext, pt: LayeredIdTable, prc: PSym, info: TLineInfo): PType # used by sigmatch for explicit generic instantiations includedFiles*: IntSet # used to detect recursive include files diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6a3b401111e0..7e800ea45e5f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2516,8 +2516,8 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType; let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar") if sym == nil: localError(c.config, info, "system needs: nimCreateFlowVar") - var bindings = initTypeMapping() - bindings.idTablePut(sym.ast[genericParamsPos][0].typ, t) + var bindings = initLayeredTypeMap() + bindings.put(sym.ast[genericParamsPos][0].typ, t) result = c.semGenerateInstance(c, sym, bindings, info) # since it's an instantiation, we unmark it as a compilerproc. Otherwise # codegen would fail: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 1bc6d31a2a8a..179176c69fc8 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -34,7 +34,7 @@ proc pushProcCon*(c: PContext; owner: PSym) = const errCannotInstantiateX = "cannot instantiate: '$1'" -iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym = +iterator instantiateGenericParamList(c: PContext, n: PNode, pt: LayeredIdTable): PSym = internalAssert c.config, n.kind == nkGenericParams for a in n.items: internalAssert c.config, a.kind == nkSym @@ -43,7 +43,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS let symKind = if q.typ.kind == tyStatic: skConst else: skType var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info) s.flags.incl {sfUsed, sfFromGeneric} - var t = idTableGet(pt, q.typ) + var t = lookup(pt, q.typ) if t == nil: if tfRetType in q.typ.flags: # keep the generic type and allow the return type to be bound @@ -220,7 +220,7 @@ proc referencesAnotherParam(n: PNode, p: PSym): bool = if referencesAnotherParam(n[i], p): return true return false -proc instantiateProcType(c: PContext, pt: TypeMapping, +proc instantiateProcType(c: PContext, pt: LayeredIdTable, prc: PSym, info: TLineInfo) = # XXX: Instantiates a generic proc signature, while at the same # time adding the instantiated proc params into the current scope. @@ -237,7 +237,7 @@ proc instantiateProcType(c: PContext, pt: TypeMapping, # will need to use openScope, addDecl, etc. #addDecl(c, prc) pushInfoContext(c.config, info) - var typeMap = initLayeredTypeMap(pt) + var typeMap = shallowCopy(pt) var cl = initTypeVars(c, typeMap, info, nil) var result = instCopyType(cl, prc.typ) let originalParams = result.n @@ -324,7 +324,7 @@ proc instantiateProcType(c: PContext, pt: TypeMapping, prc.typ = result popInfoContext(c.config) -proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType = +proc instantiateOnlyProcType(c: PContext, pt: LayeredIdTable, prc: PSym, info: TLineInfo): PType = # instantiates only the type of a given proc symbol # used by sigmatch for explicit generics # wouldn't be needed if sigmatch could handle complex cases, @@ -360,7 +360,7 @@ proc getLocalPassC(c: PContext, s: PSym): string = for p in n: extractPassc(p) -proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, +proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable, info: TLineInfo): PSym = ## Generates a new instance of a generic procedure. ## The `pt` parameter is a type-unsafe mapping table used to link generic diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f5f8fea0c608..3dad09edfd48 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1969,7 +1969,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; return result -proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode = +proc semInferredLambda(c: PContext, pt: LayeredIdTable, n: PNode): PNode = ## used for resolving 'auto' in lambdas based on their callsite var n = n let original = n[namePos].sym diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 759e8e6ab072..0b57ac22afca 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -12,7 +12,7 @@ import std / tables import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, - lineinfos, modulegraphs + lineinfos, modulegraphs, layeredtable when defined(nimPreviewSlimSystem): import std/assertions @@ -65,10 +65,6 @@ proc cacheTypeInst(c: PContext; inst: PType) = addToGenericCache(c, gt.sym, inst) type - LayeredIdTable* {.acyclic.} = ref object - topLayer*: TypeMapping - nextLayer*: LayeredIdTable - TReplTypeVars* = object c*: PContext typeMap*: LayeredIdTable # map PType to PType @@ -88,23 +84,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode -proc initLayeredTypeMap*(pt: sink TypeMapping): LayeredIdTable = - result = LayeredIdTable() - result.topLayer = pt - proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable = - result = LayeredIdTable(nextLayer: cl.typeMap, topLayer: initTable[ItemId, PType]()) - -proc lookup(typeMap: LayeredIdTable, key: PType): PType = - result = nil - var tm = typeMap - while tm != nil: - result = getOrDefault(tm.topLayer, key.itemId) - if result != nil: return - tm = tm.nextLayer - -template put(typeMap: LayeredIdTable, key, value: PType) = - typeMap.topLayer[key.itemId] = value + result = newTypeMapLayer(cl.typeMap) template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code when false: @@ -500,7 +481,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) result.flags = result.flags + newbody.flags - tfInstClearedFlags - cl.typeMap = cl.typeMap.nextLayer + setToPreviousLayer(cl.typeMap) # This is actually wrong: tgeneric_closure fails with this line: #newbody.callConv = body.callConv @@ -791,19 +772,19 @@ proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo; localCache: initTypeMapping(), typeMap: typeMap, info: info, c: p, owner: owner) -proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode; +proc replaceTypesInBody*(p: PContext, pt: LayeredIdTable, n: PNode; owner: PSym, allowMetaTypes = false, fromStaticExpr = false, expectedType: PType = nil): PNode = - var typeMap = initLayeredTypeMap(pt) + var typeMap = shallowCopy(pt) var cl = initTypeVars(p, typeMap, n.info, owner) cl.allowMetaTypes = allowMetaTypes pushInfoContext(p.config, n.info) result = replaceTypeVarsN(cl, n, expectedType = expectedType) popInfoContext(p.config) -proc prepareTypesInBody*(p: PContext, pt: TypeMapping, n: PNode; +proc prepareTypesInBody*(p: PContext, pt: LayeredIdTable, n: PNode; owner: PSym = nil): PNode = - var typeMap = initLayeredTypeMap(pt) + var typeMap = shallowCopy(pt) var cl = initTypeVars(p, typeMap, n.info, owner) pushInfoContext(p.config, n.info) result = prepareNode(cl, n) @@ -836,13 +817,13 @@ proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) = inc currPosition else: discard "cannot happen" -proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo, +proc generateTypeInstance*(p: PContext, pt: LayeredIdTable, info: TLineInfo, t: PType): PType = # Given `t` like Foo[T] # pt: Table with type mappings: T -> int # Desired result: Foo[int] # proc (x: T = 0); T -> int ----> proc (x: int = 0) - var typeMap = initLayeredTypeMap(pt) + var typeMap = shallowCopy(pt) var cl = initTypeVars(p, typeMap, info, nil) pushInfoContext(p.config, info) result = replaceTypeVarsT(cl, t) @@ -852,15 +833,15 @@ proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo, var position = 0 recomputeFieldPositions(objType, objType.n, position) -proc prepareMetatypeForSigmatch*(p: PContext, pt: TypeMapping, info: TLineInfo, +proc prepareMetatypeForSigmatch*(p: PContext, pt: LayeredIdTable, info: TLineInfo, t: PType): PType = - var typeMap = initLayeredTypeMap(pt) + var typeMap = shallowCopy(pt) var cl = initTypeVars(p, typeMap, info, nil) cl.allowMetaTypes = true pushInfoContext(p.config, info) result = replaceTypeVarsT(cl, t) popInfoContext(p.config) -template generateTypeInstance*(p: PContext, pt: TypeMapping, arg: PNode, +template generateTypeInstance*(p: PContext, pt: LayeredIdTable, arg: PNode, t: PType): untyped = generateTypeInstance(p, pt, arg.info, t) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 844db3209727..99801324acd7 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -13,7 +13,7 @@ import ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, magicsys, idents, lexer, options, parampatterns, trees, - linter, lineinfos, lowerings, modulegraphs, concepts + linter, lineinfos, lowerings, modulegraphs, concepts, layeredtable import std/[intsets, strutils, tables] @@ -56,7 +56,7 @@ type calleeScope*: int # scope depth: # is this a top-level symbol or a nested proc? call*: PNode # modified call - bindings*: TypeMapping # maps types to types + bindings*: LayeredIdTable # maps types to types magic*: TMagic # magic of operation baseTypeMatch: bool # needed for conversions from T to openarray[T] # for example @@ -114,21 +114,21 @@ proc initCandidateAux(ctx: PContext, proc initCandidate*(ctx: PContext, callee: PType): TCandidate = result = initCandidateAux(ctx, callee) result.calleeSym = nil - result.bindings = initTypeMapping() + result.bindings = initLayeredTypeMap() proc put(c: var TCandidate, key, val: PType) {.inline.} = ## Given: proc foo[T](x: T); foo(4) ## key: 'T' ## val: 'int' (typeof(4)) when false: - let old = idTableGet(c.bindings, key) + let old = lookup(c.bindings, key) if old != nil: echo "Putting ", typeToString(key), " ", typeToString(val), " and old is ", typeToString(old) if typeToString(old) == "float32": writeStackTrace() if c.c.module.name.s == "temp3": echo "binding ", key, " -> ", val - idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen)) + put(c.bindings, key, val.skipIntLit(c.c.idgen)) proc typeRel*(c: var TCandidate, f, aOrig: PType, flags: TTypeRelFlags = {}): TTypeRelation @@ -138,7 +138,7 @@ proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) = if m.c.inGenericContext > 0: # don't match yet-unresolved generic instantiations while arg != nil and arg.kind == tyGenericParam: - arg = idTableGet(m.bindings, arg) + arg = lookup(m.bindings, arg) if arg == nil or arg.containsUnresolvedType: m.state = csNoMatch return @@ -218,7 +218,7 @@ proc copyingEraseVoidParams(m: TCandidate, t: var PType) = var f = original[i] var isVoidParam = f.kind == tyVoid if not isVoidParam: - let prev = idTableGet(m.bindings, f) + let prev = lookup(m.bindings, f) if prev != nil: f = prev isVoidParam = f.kind == tyVoid if isVoidParam: @@ -246,7 +246,7 @@ proc initCandidate*(ctx: PContext, callee: PSym, result.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil result.diagnosticsEnabled = diagnosticsEnabled result.magic = result.calleeSym.magic - result.bindings = initTypeMapping() + result.bindings = initLayeredTypeMap() if binding != nil and callee.kind in routineKinds: matchGenericParams(result, binding, callee) let genericMatch = result.state @@ -270,7 +270,7 @@ proc newCandidate*(ctx: PContext, callee: PSym, proc newCandidate*(ctx: PContext, callee: PType): TCandidate = result = initCandidate(ctx, callee) -proc copyCandidate(dest: var TCandidate, src: TCandidate) = +proc shallowCopyCandidate(dest: var TCandidate, src: TCandidate) = dest.c = src.c dest.exactMatches = src.exactMatches dest.subtypeMatches = src.subtypeMatches @@ -282,7 +282,7 @@ proc copyCandidate(dest: var TCandidate, src: TCandidate) = dest.calleeSym = src.calleeSym dest.call = copyTree(src.call) dest.baseTypeMatch = src.baseTypeMatch - dest.bindings = src.bindings + dest.bindings = shallowCopy(src.bindings) proc checkGeneric(a, b: TCandidate): int = let c = a.c @@ -487,7 +487,7 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType = result = t if c.isNoCall: return while true: - result = idTableGet(c.bindings, t) + result = lookup(c.bindings, t) if result == nil: break # it's ok, no match # example code that triggers it: @@ -632,7 +632,7 @@ proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) = if fGenericOrigin != nil and last.kind == tyGenericInst and last.kidsLen-1 == fGenericOrigin.kidsLen: for i in FirstGenericParamAt..= result: if c.inheritancePenalty > -1: - minInheritance = min(minInheritance, c.inheritancePenalty) + if c.inheritancePenalty < minInheritance: + minInheritance = c.inheritancePenalty + if x > result: + bestMatch = branch + elif x > result: + bestMatch = branch result = x if result >= isIntConv: if minInheritance < maxInheritancePenalty: c.inheritancePenalty = oldInheritancePenalty + minInheritance + let oldResult = result if result > isGeneric: result = isGeneric - bindingRet result + if (oldResult in {isConvertible, isIntConv, isSubrange, isFromIntLit} or + (oldResult == isSubtype and a.isEmptyContainer)) and + bestMatch != nil: + bindingRet result, bestMatch + else: + bindingRet result else: result = isNone of tyNot: @@ -1918,8 +1935,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isGeneric of tyGenericParam: let doBindGP = doBind or trBindGenericParam in flags - var x = idTableGet(c.bindings, f) + var x = lookup(c.bindings, f) if x == nil: + var bound: PType = nil if c.callee.kind == tyGenericBody and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match @@ -1953,11 +1971,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: # check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)' if f.len > 0 and f[0].kind != tyNone: - result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam}) + c.bindings = newTypeMapLayer(c.bindings) + result = typeRel(c, f[0], a, flags + {trBindGenericParam}) + if f[0].skipTypes({tyAlias}).kind == tyOr: + bound = lookup(c.bindings, f[0]) + if bound == nil: bound = a + elif result in {isConvertible, isIntConv, isSubrange, isFromIntLit} or + (result == isSubtype and a.isEmptyContainer): + bound = f[0] + else: + bound = a + setToPreviousLayer(c.bindings) if doBindGP and result notin {isNone, isGeneric}: - let concrete = concreteType(c, a, f) + let concrete = concreteType(c, bound, f) if concrete == nil: return isNone - put(c, f, concrete) + putRecursive(c.bindings, f, concrete) if result in {isEqual, isSubtype}: result = isGeneric elif a.kind == tyTypeDesc: @@ -1969,7 +1997,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isGeneric if result == isGeneric: - var concrete = a + if bound == nil: bound = a if tfWildcard in a.flags: a.sym.transitionGenericParamToType() a.flags.excl tfWildcard @@ -1979,11 +2007,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # reason about and maintain. Refactoring typeRel to not be responsible for setting, or # at least validating, bindings can have multiple benefits. This is debatable. I'm not 100% sure. # A design that allows a proper complexity analysis of types like `tyOr` would be ideal. - concrete = concreteType(c, a, f) - if concrete == nil: + bound = concreteType(c, bound, f) + if bound == nil: return isNone if doBindGP: - put(c, f, concrete) + putRecursive(c.bindings, f, bound) elif result > isGeneric: result = isGeneric elif a.kind == tyEmpty: @@ -1998,7 +2026,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.inheritancePenalty = inheritancePenaltyOld if result > isGeneric: result = isGeneric of tyStatic: - let prev = idTableGet(c.bindings, f) + let prev = lookup(c.bindings, f) if prev == nil: if aOrig.kind == tyStatic: if c.c.inGenericContext > 0 and aOrig.n == nil and not c.isNoCall: @@ -2060,7 +2088,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.inferredTypes.add f f.add a of tyTypeDesc: - var prev = idTableGet(c.bindings, f) + var prev = lookup(c.bindings, f) if prev == nil: # proc foo(T: typedesc, x: T) # when `f` is an unresolved typedesc, `a` could be any @@ -2151,7 +2179,7 @@ proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, f: PType): PType = - result = idTableGet(m.bindings, f) + result = lookup(m.bindings, f) if result == nil: result = generateTypeInstance(c, m.bindings, arg, f) if result == nil: @@ -2371,9 +2399,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, var instantiationCounter = 0 var lastBindingCount = -1 while r in {isBothMetaConvertible, isInferred, isInferredConvertible} and - lastBindingCount != m.bindings.len and + lastBindingCount != m.bindings.currentLen and instantiationCounter < 100: - lastBindingCount = m.bindings.len + lastBindingCount = m.bindings.currentLen inc(instantiationCounter) if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds: result = c.semInferredLambda(c, m.bindings, arg) @@ -2568,7 +2596,8 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, for i in 0..