diff options
author | Zahary Karadjov <zahary@gmail.com> | 2013-05-25 15:19:03 +0300 |
---|---|---|
committer | Zahary Karadjov <zahary@gmail.com> | 2013-05-26 11:14:23 +0300 |
commit | 7fccdedcb5d1e583039b2ea2ae6564412a0f5104 (patch) | |
tree | fe426bd87d5bdab0be549729eaa20afe519495f7 /compiler | |
parent | c0931394947c4c325ae6a833eb6b0c9b410d29e5 (diff) | |
download | Nim-7fccdedcb5d1e583039b2ea2ae6564412a0f5104.tar.gz |
fixes #267
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 15 | ||||
-rw-r--r-- | compiler/semdata.nim | 5 | ||||
-rw-r--r-- | compiler/seminst.nim | 103 | ||||
-rw-r--r-- | compiler/semtypes.nim | 156 | ||||
-rw-r--r-- | compiler/types.nim | 2 |
5 files changed, 184 insertions, 97 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index d4d5bce9c..e35bf25ef 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -365,12 +365,8 @@ type tfFromGeneric, # type is an instantiation of a generic; this is needed # because for instantiations of objects, structural # type equality has to be used - tfInstantiated, # XXX: used to mark generic params after instantiation. - # if the concrete type happens to be an implicit generic - # this can lead to invalid proc signatures in the second - # pass of semProcTypeNode performed after instantiation. - # this won't be needed if we don't perform this redundant - # second pass (stay tuned). + tfUnresolved, # marks unresolved typedesc params: e.g. + # proc foo(T: typedesc, list: seq[T]): var T tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) tfAll, # type class requires all constraints to be met (default) @@ -1007,8 +1003,8 @@ proc NewType(kind: TTypeKind, owner: PSym): PType = result.size = - 1 result.align = 2 # default alignment result.id = getID() - when debugIds: - RegisterId(result) + when debugIds: + RegisterId(result) #if result.id < 2000 then # MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id)) @@ -1045,7 +1041,6 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType = if keepId: result.id = t.id else: - result.id = getID() when debugIds: RegisterId(result) result.sym = t.sym # backend-info should not be copied @@ -1290,7 +1285,7 @@ proc isGenericRoutine*(s: PSym): bool = of skProc, skTemplate, skMacro, skIterator, skMethod, skConverter: result = s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty else: nil - + proc isRoutine*(s: PSym): bool {.inline.} = result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter} diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 4c94d0812..127842a5a 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -207,6 +207,11 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) +proc newTypeWithSons*(c: PContext, kind: TTypeKind, + sons: seq[PType]): PType = + result = newType(kind, getCurrOwner()) + result.sons = sons + proc errorType*(c: PContext): PType = ## creates a type representing an error state result = newTypeS(tyError, c) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 9dc99d173..35ed00965 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -38,7 +38,6 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, #t = instGenericContainer(c, a, t) t = generateTypeInstance(c, pt, a, t) #t = ReplaceTypeVarsT(cl, t) - t.flags.incl tfInstantiated s.typ = t addDecl(c, s) entry.concreteTypes[i] = t @@ -84,11 +83,28 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = else: for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap) +proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) + proc instantiateBody(c: PContext, n: PNode, result: PSym) = if n.sons[bodyPos].kind != nkEmpty: # add it here, so that recursive generic procs are possible: addDecl(c, result) pushProcCon(c, result) + # add params to scope + let origFormalParams = result.typ.n + result.typ.n = newNodeI(nkFormalParams, + origFormalParams.info, + origFormalParams.len) + result.typ.n.sons[0] = copyNode(origFormalParams.sons[0]) + for i in 1 .. <result.typ.len: + let origParam = origFormalParams[i].sym + var param = copySym(origParam) + result.typ.n.sons[i] = newSymNode(param) + param.typ = result.typ.sons[i] + param.ast = origParam.ast + param.owner = result + addParamOrResult(c, param, result.kind) + # debug result.typ.n maybeAddResult(c, result, n) var b = n.sons[bodyPos] var symMap: TIdTable @@ -123,7 +139,71 @@ proc sideEffectsCheck(c: PContext, s: PSym) = s.ast.sons[genericParamsPos].kind == nkEmpty: c.threadEntries.add(s) -proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, +proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType = + var cl: TReplTypeVars + InitIdTable(cl.symMap) + InitIdTable(cl.typeMap) + cl.info = info + cl.c = c + result = ReplaceTypeVarsT(cl, header) + +proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = + result = instGenericContainer(c, n.info, header) + +proc fixupProcTypeR(c: PContext, genericType: PType, + inst: TInstantiation): PType = + result = genericType + if result == nil: return + + case genericType.kind + of tyGenericParam, tyTypeClass: + result = inst.concreteTypes[genericType.sym.position] + of tyTypeDesc: + result = inst.concreteTypes[genericType.sym.position] + if tfUnresolved in genericType.flags: + result = result.sons[0] + of tyExpr: + result = inst.concreteTypes[genericType.sym.position] + of tyOpenArray, tyArray, tySet, tySequence, tyTuple, tyProc, + tyPtr, tyVar, tyRef, tyOrdinal, tyRange, tyVarargs: + if genericType.sons == nil: return + for i in 0 .. <genericType.sons.len: + let changed = fixupProcTypeR(c, genericType.sons[i], inst) + if changed != genericType.sons[i]: + if result == genericType: + # the first detected change initializes the result + result = copyType(genericType, genericType.owner, true) + if genericType.n != nil: + result.n = copyTree(genericType.n) + result.sons[i] = changed + if result.n != nil: + if result.n.kind == nkRecList: + result.n.sons[i].typ = changed + if result.n.kind == nkFormalParams: + if i == 0: + nil + else: + let origParam = result.n.sons[i].sym + var param = copySym(origParam) + param.typ = changed + param.ast = origParam.ast + result.n.sons[i] = newSymNode(param) + + of tyGenericInvokation: + result = newTypeWithSons(c, tyGenericInvokation, genericType.sons) + for i in 1 .. <genericType.sons.len: + result.sons[i] = fixupProcTypeR(c, result.sons[i], inst) + result = instGenericContainer(c, getInfoContext(-1), result) + else: + nil + +proc fixupProcType(c: PContext, genericType: PType, + inst: TInstantiation): PType = + result = copyType(genericType, genericType.owner, false) + for i in 0 .. <result.sons.len: + result.sons[i] = fixupProcTypeR(c, result.sons[i], inst) + +proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = # no need to instantiate generic templates/macros: if fn.kind in {skTemplate, skMacro}: return fn @@ -152,16 +232,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, var entry = TInstantiation.new entry.sym = result instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) + result.typ = fixupProcType(c, fn.typ, entry[]) n.sons[genericParamsPos] = ast.emptyNode - # semantic checking for the parameters: - if n.sons[paramsPos].kind != nkEmpty: - removeDefaultParamValues(n.sons[ParamsPos]) - semParamList(c, n.sons[ParamsPos], nil, result) - else: - result.typ = newTypeS(tyProc, c) - rawAddSon(result.typ, nil) - result.typ.callConv = fn.typ.callConv - if result.kind == skIterator: result.typ.flags.incl(tfIterator) var oldPrc = GenericCacheGet(fn, entry[]) if oldPrc == nil: fn.procInstCache.safeAdd(entry) @@ -182,12 +254,5 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, c.friendModule = oldFriend dec(c.InstCounter) if result.kind == skMethod: finishMethod(c, result) - -proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = - var cl: TReplTypeVars - InitIdTable(cl.symMap) - InitIdTable(cl.typeMap) - cl.info = n.info - cl.c = c - result = ReplaceTypeVarsT(cl, header) + diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 658b3507f..c975abb26 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -216,7 +216,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = else: LocalError(n.info, errXExpectsOneTypeParam, "ordinal") result = newOrPrevType(tyError, prev, c) - + proc semTypeIdent(c: PContext, n: PNode): PSym = if n.kind == nkSym: result = n.sym @@ -235,7 +235,9 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if result.typ.sym == nil: LocalError(n.info, errTypeExpected) return errorSym(c, n) - return result.typ.sym + result = result.typ.sym.copySym + result.typ = copyType(result.typ, result.typ.owner, true) + result.typ.flags.incl tfUnresolved if result.kind != skType: # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter @@ -573,81 +575,95 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = else: if sfGenSym notin param.flags: addDecl(c, param) -proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind): - tuple[typ: PType, id: PIdent] = - # if typ is not-nil, the param should be turned into a generic param - # if id is not nil, the generic param will bind just once (see below) +proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, + paramType: PType, paramName: string, + info: TLineInfo, anon = false): PType = + if procKind in {skMacro, skTemplate}: + # generic param types in macros and templates affect overload + # resolution, but don't work as generic params when it comes + # to proc instantiation. We don't need to lift such params here. + return + + proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = + let finalTypId = if typId != nil: typId + else: getIdent(paramName & ":type") + # is this a bindOnce type class already present in the param list? + for i in countup(0, genericParams.len - 1): + if genericParams.sons[i].sym.name.id == finalTypId.id: + return genericParams.sons[i].typ + + var s = newSym(skType, finalTypId, getCurrOwner(), info) + if typId == nil: s.flags.incl(sfAnon) + s.linkTo(typeClass) + s.position = genericParams.len + genericParams.addSon(newSymNode(s)) + result = typeClass + + # XXX: There are codegen errors if this is turned into a nested proc + template liftingWalk(typ: PType, anonFlag = false): expr = + liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) + #proc liftingWalk(paramType: PType, anon = false): PType = + + var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name + else: nil + + template addImplicitGeneric(e: expr): expr = + addImplicitGenericImpl(e, paramTypId) + case paramType.kind: of tyExpr: if paramType.sonsLen == 0: # proc(a, b: expr) # no constraints, treat like generic param - result.typ = newTypeS(tyGenericParam, c) + result = addImplicitGeneric(newTypeS(tyGenericParam, c)) else: # proc(a: expr{string}, b: expr{nkLambda}) # overload on compile time values and AST trees - result.typ = newTypeS(tyExpr, c) - result.typ.sons = paramType.sons + result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons)) of tyTypeDesc: - if tfInstantiated notin paramType.flags: - result.typ = newTypeS(tyTypeDesc, c) - result.typ.sons = paramType.sons + if tfUnresolved notin paramType.flags: + result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) of tyDistinct: - result = paramTypeClass(c, paramType.lastSon, procKind) - # disable the bindOnce behavior for the type class - result.id = nil - return + if paramType.sonsLen == 1: + # disable the bindOnce behavior for the type class + result = liftingWalk(paramType.sons[0], true) + of tySequence, tySet, tyArray, tyOpenArray: + # XXX: this is a bit strange, but proc(s: seq) + # produces tySequence(tyGenericParam, null). + # This also seems to be true when creating aliases + # like: type myseq = distinct seq. + # Maybe there is another better place to associate + # the seq type class with the seq identifier. + if paramType.lastSon == nil: + let typ = c.newTypeWithSons(tyTypeClass, @[newTypeS(paramType.kind, c)]) + result = addImplicitGeneric(typ) + else: + for i in 0 .. <paramType.sons.len: + var lifted = liftingWalk(paramType.sons[i]) + if lifted != nil: + paramType.sons[i] = lifted + result = paramType of tyGenericBody: # type Foo[T] = object # proc x(a: Foo, b: Foo) - result.typ = newTypeS(tyTypeClass, c) - result.typ.addSonSkipIntLit(paramType) + var typ = newTypeS(tyTypeClass, c) + typ.addSonSkipIntLit(paramType) + result = addImplicitGeneric(typ) + of tyGenericInst: + for i in 1 .. (paramType.sons.len - 2): + var lifted = liftingWalk(paramType.sons[i]) + if lifted != nil: + paramType.sons[i] = lifted + result = paramType + + if result != nil: + result.kind = tyGenericInvokation + result.sons.setLen(result.sons.len - 1) of tyTypeClass: - result.typ = copyType(paramType, getCurrOwner(), false) + result = addImplicitGeneric(copyType(paramType, getCurrOwner(), false)) else: nil - # bindOnce by default - if paramType.sym != nil: result.id = paramType.sym.name - -proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, - paramType: PType, paramName: string, - info: TLineInfo): PType = - result = paramType - if procKind in {skMacro, skTemplate}: - # generic param types in macros and templates affect overload - # resolution, but don't work as generic params when it comes - # to proc instantiation. We don't need to lift such params here. - return - ## Params having implicit generic types or pseudo types such as 'expr' - ## need to be added to the generic params lists. - ## 'expr' is different from 'expr{string}' so we must first call - ## paramTypeClass to get the actual type we are going to use. - var (typeClass, paramTypId) = paramTypeClass(c, paramType, procKind) - let isAnon = paramTypId == nil - if typeClass != nil: - if isAnon: paramTypId = getIdent(paramName & ":type") - if genericParams == nil: - # genericParams is nil when the proc is being instantiated - # the resolved type will be in scope then - let s = searchInScopes(c, paramTypId) - # tests/run/tinterf triggers this: - if s != nil: result = s.typ - else: - LocalError(info, errCannotInstantiateX, paramName) - result = errorType(c) - else: - block addImplicitGeneric: - # is this a bindOnce type class already present in the param list? - for i in countup(0, genericParams.len - 1): - if genericParams.sons[i].sym.name.id == paramTypId.id: - result = genericParams.sons[i].typ - break addImplicitGeneric - var s = newSym(skType, paramTypId, getCurrOwner(), info) - if isAnon: s.flags.incl(sfAnon) - s.linkTo(typeClass) - s.position = genericParams.len - genericParams.addSon(newSymNode(s)) - result = typeClass + # result = liftingWalk(paramType) proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = if n.kind == nkCurlyExpr: @@ -686,10 +702,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, length = sonsLen(a) hasType = a.sons[length-2].kind != nkEmpty hasDefault = a.sons[length-1].kind != nkEmpty - if hasType: typ = semParamType(c, a.sons[length-2], constraint) - + if hasDefault: def = semExprWithType(c, a.sons[length-1]) # check type compability between def.typ and typ: @@ -707,8 +722,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) - var finalType = liftParamType(c, kind, genericParams, typ, - arg.name.s, arg.info).skipIntLit + let lifted = liftParamType(c, kind, genericParams, typ, + arg.name.s, arg.info) + let finalType = if lifted != nil: lifted else: typ.skipIntLit arg.typ = finalType arg.position = counter arg.constraint = constraint @@ -726,11 +742,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # compiler only checks for 'nil': if skipTypes(r, {tyGenericInst}).kind != tyEmpty: if r.sym == nil or sfAnon notin r.sym.flags: - r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) + let lifted = liftParamType(c, kind, genericParams, r, "result", + n.sons[0].info) + if lifted != nil: r = lifted r.flags.incl tfRetType result.sons[0] = skipIntLit(r) res.typ = result.sons[0] - + proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = checkMinSonsLen(n, 1) var length = sonsLen(n) @@ -1012,6 +1030,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if n.kind != nkGenericParams: illFormedAst(n) return + var position = 0 for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if a.kind != nkIdentDefs: illFormedAst(n) @@ -1049,6 +1068,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if def.kind != nkEmpty: s.ast = def s.typ.sym = s if father != nil: addSonSkipIntLit(father, s.typ) - s.position = i + s.position = position + inc position addSon(result, newSymNode(s)) if sfGenSym notin s.flags: addDecl(c, s) diff --git a/compiler/types.nim b/compiler/types.nim index 3096b73c8..731c1f12a 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -982,6 +982,8 @@ proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool = # if the loop finished without returning, either all constraints matched # or none of them matched. result = if tfAny in typeClass.flags: false else: true + if result == true: + IdTablePut(bindings, typeClass, t) proc matchTypeClass*(typeClass, typ: PType): bool = var bindings: TIdTable |