# # # The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # this module does the semantic checking of type declarations # included from sem.nim proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = if prev == nil: result = newTypeS(kind, c) else: result = prev if result.kind == tyForward: result.kind = kind proc newConstraint(c: PContext, k: TTypeKind): PType = result = newTypeS(tyBuiltInTypeClass, c) result.addSonSkipIntLit(newTypeS(k, c)) proc semEnum(c: PContext, n: PNode, prev: PType): PType = if n.sonsLen == 0: return newConstraint(c, tyEnum) elif n.sonsLen == 1: # don't create an empty tyEnum; fixes #3052 return errorType(c) var counter, x: BiggestInt e: PSym base: PType counter = 0 base = nil result = newOrPrevType(tyEnum, prev, c) result.n = newNodeI(nkEnumTy, n.info) checkMinSonsLen(n, 1) if n.sons[0].kind != nkEmpty: base = semTypeNode(c, n.sons[0].sons[0], nil) if base.kind != tyEnum: localError(n.sons[0].info, errInheritanceOnlyWithEnums) counter = lastOrd(base) + 1 rawAddSon(result, base) let isPure = result.sym != nil and sfPure in result.sym.flags var hasNull = false for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkEnumFieldDef: e = newSymS(skEnumField, n.sons[i].sons[0], c) var v = semConstExpr(c, n.sons[i].sons[1]) var strVal: PNode = nil case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind of tyTuple: if sonsLen(v) == 2: strVal = v.sons[1] # second tuple part is the string value if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}: x = getOrdValue(v.sons[0]) # first tuple part is the ordinal else: localError(strVal.info, errStringLiteralExpected) else: localError(v.info, errWrongNumberOfVariables) of tyString, tyCString: strVal = v x = counter else: x = getOrdValue(v) if i != 1: if x != counter: incl(result.flags, tfEnumHasHoles) if x < counter: localError(n.sons[i].info, errInvalidOrderInEnumX, e.name.s) x = counter e.ast = strVal # might be nil counter = x of nkSym: e = n.sons[i].sym of nkIdent, nkAccQuoted: e = newSymS(skEnumField, n.sons[i], c) else: illFormedAst(n[i]) e.typ = result e.position = int(counter) if e.position == 0: hasNull = true if result.sym != nil and sfExported in result.sym.flags: incl(e.flags, sfUsed) incl(e.flags, sfExported) if not isPure: strTableAdd(c.module.tab, e) addSon(result.n, newSymNode(e)) styleCheckDef(e) if sfGenSym notin e.flags and not isPure: addDecl(c, e) inc(counter) if not hasNull: incl(result.flags, tfNeedsInit) proc semSet(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tySet, prev, c) if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) addSonSkipIntLit(result, base) if base.kind == tyGenericInst: base = lastSon(base) if base.kind != tyGenericParam: if not isOrdinalType(base): localError(n.info, errOrdinalTypeExpected) elif lengthOrd(base) > MaxSetElements: localError(n.info, errSetTooBig) else: localError(n.info, errXExpectsOneTypeParam, "set") addSonSkipIntLit(result, errorType(c)) proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, prev: PType): PType = result = newOrPrevType(kind, prev, c) if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) addSonSkipIntLit(result, base) else: localError(n.info, errXExpectsOneTypeParam, kindStr) addSonSkipIntLit(result, errorType(c)) proc semVarargs(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyVarargs, prev, c) if sonsLen(n) == 2 or sonsLen(n) == 3: var base = semTypeNode(c, n.sons[1], nil) addSonSkipIntLit(result, base) if sonsLen(n) == 3: result.n = newIdentNode(considerQuotedIdent(n.sons[2]), n.sons[2].info) else: localError(n.info, errXExpectsOneTypeParam, "varargs") addSonSkipIntLit(result, errorType(c)) proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = if n.len < 1: result = newConstraint(c, kind) else: let isCall = ord(n.kind in nkCallKinds) let n = if n[0].kind == nkBracket: n[0] else: n checkMinSonsLen(n, 1) var base = semTypeNode(c, n.lastSon, nil) result = newOrPrevType(kind, prev, c) # check every except the last is an object: for i in isCall .. n.len-2: let region = semTypeNode(c, n[i], nil) if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}: message n[i].info, errGenerated, "region needs to be an object type" addSonSkipIntLit(result, region) addSonSkipIntLit(result, base) proc semVarType(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 1: result = newOrPrevType(tyVar, prev, c) var base = semTypeNode(c, n.sons[0], nil) if base.kind == tyVar: localError(n.info, errVarVarTypeNotAllowed) base = base.sons[0] addSonSkipIntLit(result, base) else: result = newConstraint(c, tyVar) proc semDistinct(c: PContext, n: PNode, prev: PType): PType = if n.len == 0: return newConstraint(c, tyDistinct) result = newOrPrevType(tyDistinct, prev, c) addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) if n.len > 1: result.n = n[1] proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = assert isRange(n) checkSonsLen(n, 3) result = newOrPrevType(tyRange, prev, c) result.n = newNodeI(nkRange, n.info) if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): localError(n.info, errRangeIsEmpty) var range: array[2, PNode] range[0] = semExprWithType(c, n[1], {efDetermineType}) range[1] = semExprWithType(c, n[2], {efDetermineType}) var rangeT: array[2, PType] for i in 0..1: rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): localError(n.info, errPureTypeMismatch) elif not rangeT[0].isOrdinalType: localError(n.info, errOrdinalTypeExpected) elif enumHasHoles(rangeT[0]): localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s) for i in 0..1: if hasGenericArguments(range[i]): result.n.addSon makeStaticExpr(c, range[i]) else: result.n.addSon semConstExpr(c, range[i]) if weakLeValue(result.n[0], result.n[1]) == impNo: localError(n.info, errRangeIsEmpty) addSonSkipIntLit(result, rangeT[0]) proc semRange(c: PContext, n: PNode, prev: PType): PType = result = nil if sonsLen(n) == 2: if isRange(n[1]): result = semRangeAux(c, n[1], prev) let n = result.n if n.sons[0].kind in {nkCharLit..nkUInt64Lit} and n.sons[0].intVal > 0: incl(result.flags, tfNeedsInit) elif n.sons[1].kind in {nkCharLit..nkUInt64Lit} and n.sons[1].intVal < 0: incl(result.flags, tfNeedsInit) elif n.sons[0].kind in {nkFloatLit..nkFloat64Lit} and n.sons[0].floatVal > 0.0: incl(result.flags, tfNeedsInit) elif n.sons[1].kind in {nkFloatLit..nkFloat64Lit} and n.sons[1].floatVal < 0.0: incl(result.flags, tfNeedsInit) else: localError(n.sons[0].info, errRangeExpected) result = newOrPrevType(tyError, prev, c) else: localError(n.info, errXExpectsOneTypeParam, "range") result = newOrPrevType(tyError, prev, c) proc semArrayIndex(c: PContext, n: PNode): PType = if isRange(n): result = semRangeAux(c, n, nil) else: let e = semExprWithType(c, n, {efDetermineType}) if e.typ.kind == tyFromExpr: result = makeRangeWithStaticExpr(c, e.typ.n) elif e.kind in {nkIntLit..nkUInt64Lit}: result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) elif e.kind == nkSym and e.typ.kind == tyStatic: if e.sym.ast != nil: return semArrayIndex(c, e.sym.ast) if not isOrdinalType(e.typ.lastSon): localError(n[1].info, errOrdinalTypeExpected) result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved elif e.kind in nkCallKinds and hasGenericArguments(e): if not isOrdinalType(e.typ): localError(n[1].info, errOrdinalTypeExpected) # This is an int returning call, depending on an # yet unknown generic param (see tgenericshardcases). # We are going to construct a range type that will be # properly filled-out in semtypinst (see how tyStaticExpr # is handled there). result = makeRangeWithStaticExpr(c, e) elif e.kind == nkIdent: result = e.typ.skipTypes({tyTypeDesc}) else: let x = semConstExpr(c, e) if x.kind in {nkIntLit..nkUInt64Lit}: result = makeRangeType(c, 0, x.intVal-1, n.info, x.typ.skipTypes({tyTypeDesc})) else: result = x.typ.skipTypes({tyTypeDesc}) #localError(n[1].info, errConstExprExpected) proc semArray(c: PContext, n: PNode, prev: PType): PType = var base: PType if sonsLen(n) == 3: # 3 = length(array indx base) let indx = semArrayIndex(c, n[1]) var indxB = indx if indxB.kind == tyGenericInst: indxB = lastSon(indxB) if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}: if not isOrdinalType(indxB): localError(n.sons[1].info, errOrdinalTypeExpected) elif enumHasHoles(indxB): localError(n.sons[1].info, errEnumXHasHoles, typeToString(indxB.skipTypes({tyRange}))) base = semTypeNode(c, n.sons[2], nil) # ensure we only construct a tyArray when there was no error (bug #3048): result = newOrPrevType(tyArray, prev, c) addSonSkipIntLit(result, indx) addSonSkipIntLit(result, base) else: localError(n.info, errArrayExpectsTwoTypeParams) result = newOrPrevType(tyError, prev, c) proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyOrdinal, prev, c) if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) if base.kind != tyGenericParam: if not isOrdinalType(base): localError(n.sons[1].info, errOrdinalTypeExpected) addSonSkipIntLit(result, base) 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 else: when defined(nimfix): result = pickSym(c, n, skType) if result.isNil: result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) else: result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if result != nil: markUsed(n.info, result) styleCheckUse(n.info, result) if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? # it's not bound when it's used multiple times in the # proc signature for example if c.inGenericInst > 0: let bound = result.typ.sons[0].sym if bound != nil: return bound return result if result.typ.sym == nil: localError(n.info, errTypeExpected) return errorSym(c, n) result = result.typ.sym.copySym result.typ = copyType(result.typ, result.typ.owner, true) result.typ.flags.incl tfUnresolved if result.kind == skGenericParam: if result.typ.kind == tyGenericParam and result.typ.len == 0 and tfWildcard in result.typ.flags: # collapse the wild-card param to a type result.kind = skType result.typ.flags.excl tfWildcard return else: localError(n.info, errTypeExpected) return errorSym(c, n) if result.kind != skType: # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter var amb = initOverloadIter(ov, c, n) while amb != nil and amb.kind != skType: amb = nextOverloadIter(ov, c, n) if amb != nil: result = amb else: if result.kind != skError: localError(n.info, errTypeExpected) return errorSym(c, n) if result.typ.kind != tyGenericParam: # XXX get rid of this hack! var oldInfo = n.info when defined(useNodeIds): let oldId = n.id reset(n[]) when defined(useNodeIds): n.id = oldId n.kind = nkSym n.sym = result n.info = oldInfo n.typ = result.typ else: localError(n.info, errIdentifierExpected) result = errorSym(c, n) proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 0: localError(n.info, errTypeExpected) result = newOrPrevType(tyTuple, prev, c) for i in countup(0, sonsLen(n) - 1): addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil)) proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType result = newOrPrevType(tyTuple, prev, c) result.n = newNodeI(nkRecList, n.info) var check = initIntSet() var counter = 0 for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var length = sonsLen(a) if a.sons[length - 2].kind != nkEmpty: typ = semTypeNode(c, a.sons[length - 2], nil) else: localError(a.info, errTypeExpected) typ = errorType(c) if a.sons[length - 1].kind != nkEmpty: localError(a.sons[length - 1].info, errInitHereNotAllowed) for j in countup(0, length - 3): var field = newSymG(skField, a.sons[j], c) field.typ = typ field.position = counter inc(counter) if containsOrIncl(check, field.name.id): localError(a.sons[j].info, errAttemptToRedefine, field.name.s) else: addSon(result.n, newSymNode(field)) addSonSkipIntLit(result, typ) if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field) proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym = # identifier with visibility if n.kind == nkPostfix: if sonsLen(n) == 2 and n.sons[0].kind == nkIdent: # for gensym'ed identifiers the identifier may already have been # transformed to a symbol and we need to use that here: result = newSymG(kind, n.sons[1], c) var v = n.sons[0].ident if sfExported in allowed and v.id == ord(wStar): incl(result.flags, sfExported) else: localError(n.sons[0].info, errInvalidVisibilityX, v.s) else: illFormedAst(n) else: result = newSymG(kind, n, c) proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym = if n.kind == nkPragmaExpr: checkSonsLen(n, 2) result = semIdentVis(c, kind, n.sons[0], allowed) case kind of skType: # process pragmas later, because result.typ has not been set yet discard of skField: pragma(c, result, n.sons[1], fieldPragmas) of skVar: pragma(c, result, n.sons[1], varPragmas) of skLet: pragma(c, result, n.sons[1], letPragmas) of skConst: pragma(c, result, n.sons[1], constPragmas) else: discard else: result = semIdentVis(c, kind, n, allowed) if gCmd == cmdPretty: styleCheckDef(n.info, result) proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = let ex = t[branchIndex][currentEx].skipConv for i in countup(1, branchIndex): for j in countup(0, sonsLen(t.sons[i]) - 2): if i == branchIndex and j == currentEx: break if overlap(t.sons[i].sons[j].skipConv, ex): localError(ex.info, errDuplicateCaseLabel) proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode = checkMinSonsLen(t, 1) let ac = semConstExpr(c, a) let bc = semConstExpr(c, b) let at = fitNode(c, t.sons[0].typ, ac).skipConvTakeType let bt = fitNode(c, t.sons[0].typ, bc).skipConvTakeType result = newNodeI(nkRange, a.info) result.add(at) result.add(bt) if emptyRange(ac, bc): localError(b.info, errRangeIsEmpty) else: covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1 proc semCaseBranchRange(c: PContext, t, b: PNode, covered: var BiggestInt): PNode = checkSonsLen(b, 3) result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) proc semCaseBranchSetElem(c: PContext, t, b: PNode, covered: var BiggestInt): PNode = if isRange(b): checkSonsLen(b, 3) result = semBranchRange(c, t, b.sons[1], b.sons[2], covered) elif b.kind == nkRange: checkSonsLen(b, 2) result = semBranchRange(c, t, b.sons[0], b.sons[1], covered) else: result = fitNode(c, t.sons[0].typ, b) inc(covered) proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, covered: var BiggestInt) = for i in countup(0, sonsLen(branch) - 2): var b = branch.sons[i] if b.kind == nkRange: branch.sons[i] = b elif isRange(b): branch.sons[i] = semCaseBranchRange(c, t, b, covered) else: # constant sets and arrays are allowed: var r = semConstExpr(c, b) if r.kind in {nkCurly, nkBracket} and len(r) == 0 and sonsLen(branch)==2: # discarding ``{}`` and ``[]`` branches silently delSon(branch, 0) return elif r.kind notin {nkCurly, nkBracket} or len(r) == 0: checkMinSonsLen(t, 1) branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r)) inc(covered) else: # first element is special and will overwrite: branch.sons[i]: branch.sons[i] = semCaseBranchSetElem(c, t, r[0], covered) # other elements have to be added to ``branch`` for j in 1 .. 0x00007FFF: localError(n.info, errLenXinvalid, a.sons[0].sym.name.s) var chckCovered = true for i in countup(1, sonsLen(n) - 1): var b = copyTree(n.sons[i]) addSon(a, b) case n.sons[i].kind of nkOfBranch: checkMinSonsLen(b, 2) semCaseBranch(c, a, b, i, covered) of nkElse: chckCovered = false checkSonsLen(b, 1) else: illFormedAst(n) delSon(b, sonsLen(b) - 1) semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype) if chckCovered and (covered != lengthOrd(a.sons[0].typ)): localError(a.info, errNotAllCasesCovered) addSon(father, a) proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType) = if n == nil: return case n.kind of nkRecWhen: var branch: PNode = nil # the branch to take for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] if it == nil: illFormedAst(n) var idx = 1 case it.kind of nkElifBranch: checkSonsLen(it, 2) if c.inGenericContext == 0: var e = semConstBoolExpr(c, it.sons[0]) if e.kind != nkIntLit: internalError(e.info, "semRecordNodeAux") elif e.intVal != 0 and branch == nil: branch = it.sons[1] else: it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) of nkElse: checkSonsLen(it, 1) if branch == nil: branch = it.sons[0] idx = 0 else: illFormedAst(n) if c.inGenericContext > 0: # use a new check intset here for each branch: var newCheck: IntSet assign(newCheck, check) var newPos = pos var newf = newNodeI(nkRecList, n.info) semRecordNodeAux(c, it.sons[idx], newCheck, newPos, newf, rectype) it.sons[idx] = if newf.len == 1: newf[0] else: newf if c.inGenericContext > 0: addSon(father, n) elif branch != nil: semRecordNodeAux(c, branch, check, pos, father, rectype) of nkRecCase: semRecordCase(c, n, check, pos, father, rectype) of nkNilLit: if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info)) of nkRecList: # attempt to keep the nesting at a sane level: var a = if father.kind == nkRecList: father else: copyNode(n) for i in countup(0, sonsLen(n) - 1): semRecordNodeAux(c, n.sons[i], check, pos, a, rectype) if a != father: addSon(father, a) of nkIdentDefs: checkMinSonsLen(n, 3) var length = sonsLen(n) var a: PNode if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info) else: a = ast.emptyNode if n.sons[length-1].kind != nkEmpty: localError(n.sons[length-1].info, errInitHereNotAllowed) var typ: PType if n.sons[length-2].kind == nkEmpty: localError(n.info, errTypeExpected) typ = errorType(c) else: typ = semTypeNode(c, n.sons[length-2], nil) propagateToOwner(rectype, typ) let rec = rectype.sym for i in countup(0, sonsLen(n)-3): var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) suggestSym(n.sons[i].info, f) f.typ = typ f.position = pos if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and (f.loc.r == nil): f.loc.r = rope(f.name.s) f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) inc(pos) if containsOrIncl(check, f.name.id): localError(n.sons[i].info, errAttemptToRedefine, f.name.s) if a.kind == nkEmpty: addSon(father, newSymNode(f)) else: addSon(a, newSymNode(f)) styleCheckDef(f) if a.kind != nkEmpty: addSon(father, a) of nkEmpty: discard else: illFormedAst(n) proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int, n: PNode) = case n.kind of nkRecCase: if (n.sons[0].kind != nkSym): internalError(n.info, "addInheritedFieldsAux") addInheritedFieldsAux(c, check, pos, n.sons[0]) for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkOfBranch, nkElse: addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i])) else: internalError(n.info, "addInheritedFieldsAux(record case branch)") of nkRecList: for i in countup(0, sonsLen(n) - 1): addInheritedFieldsAux(c, check, pos, n.sons[i]) of nkSym: incl(check, n.sym.name.id) inc(pos) else: internalError(n.info, "addInheritedFieldsAux()") proc skipGenericInvocation(t: PType): PType {.inline.} = result = t if result.kind == tyGenericInvocation: result = result.sons[0] while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr}: result = lastSon(result) proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, obj: PType) = assert obj.kind == tyObject if (sonsLen(obj) > 0) and (obj.sons[0] != nil): addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvocation) addInheritedFieldsAux(c, check, pos, obj.n) proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if n.sonsLen == 0: return newConstraint(c, tyObject) var check = initIntSet() var pos = 0 var base: PType = nil # n.sons[0] contains the pragmas (if any). We process these later... checkSonsLen(n, 3) if n.sons[1].kind != nkEmpty: base = skipTypesOrNil(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) if base.isNil: localError(n.info, errIllegalRecursionInTypeX, "object") else: var concreteBase = skipGenericInvocation(base) if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: addInheritedFields(c, check, pos, concreteBase) else: if concreteBase.kind != tyError: localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) base = nil if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") result = newOrPrevType(tyObject, prev, c) rawAddSon(result, base) result.n = newNodeI(nkRecList, n.info) semRecordNodeAux(c, n.sons[2], check, pos, result.n, result) if n.sons[0].kind != nkEmpty: # dummy symbol for `pragma`: var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c) s.typ = result pragma(c, s, n.sons[0], typePragmas) if base == nil and tfInheritable notin result.flags: incl(result.flags, tfFinal) proc findEnforcedStaticType(t: PType): PType = # This handles types such as `static[T] and Foo`, # which are subset of `static[T]`, hence they could # be treated in the same way if t.kind == tyStatic: return t if t.kind == tyAnd: for s in t.sons: let t = findEnforcedStaticType(s) if t != nil: return t proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = if kind == skMacro: let staticType = findEnforcedStaticType(param.typ) if staticType != nil: var a = copySym(param) a.typ = staticType.base addDecl(c, a) elif param.typ.kind == tyTypeDesc: addDecl(c, param) else: # within a macro, every param has the type NimNode! let nn = if getCompilerProc("NimNode") != nil: getSysSym"NimNode" else: getSysSym"PNimrodNode" var a = copySym(param) a.typ = nn.typ addDecl(c, a) else: if sfGenSym notin param.flags: addDecl(c, param) let typedescId = getIdent"typedesc" template shouldHaveMeta(t) = internalAssert tfHasMeta in t.flags # result.lastSon.flags.incl tfHasMeta proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType: PType, paramName: string, info: TLineInfo, anon = false): PType = if paramType == nil: return # (e.g. proc return type) proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = if genericParams == nil: # This happens with anonymous proc types appearing in signatures # XXX: we need to lift these earlier return 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 let owner = if typeClass.sym != nil: typeClass.sym else: getCurrOwner() var s = newSym(skType, finalTypId, owner, info) if typId == nil: s.flags.incl(sfAnon) s.linkTo(typeClass) typeClass.flags.incl tfImplicitTypeParam s.position = genericParams.len genericParams.addSon(newSymNode(s)) result = typeClass addDecl(c, s) # 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 maybeLift(typ: PType): expr = let lifted = liftingWalk(typ) (if lifted != nil: lifted else: typ) template addImplicitGeneric(e: expr): expr = addImplicitGenericImpl(e, paramTypId) case paramType.kind: of tyAnything: result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil) of tyStatic: # proc(a: expr{string}, b: expr{nkLambda}) # overload on compile time values and AST trees if paramType.n != nil: return # this is a concrete type if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift if base.isMetaType and procKind == skMacro: localError(info, errMacroBodyDependsOnGenericTypes, paramName) result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base])) result.flags.incl({tfHasStatic, tfUnresolved}) of tyTypeDesc: if tfUnresolved notin paramType.flags: # naked typedescs are not bindOnce types if paramType.base.kind == tyNone and paramTypId != nil and paramTypId.id == typedescId.id: paramTypId = nil result = addImplicitGeneric( c.newTypeWithSons(tyTypeDesc, @[paramType.base])) of tyDistinct: if paramType.sonsLen == 1: # disable the bindOnce behavior for the type class result = liftingWalk(paramType.sons[0], true) of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, tyProc: # XXX: this is a bit strange, but proc(s: seq) # produces tySequence(tyGenericParam, tyNone). # 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.kind == tySequence and paramType.lastSon.kind == tyNone: let typ = c.newTypeWithSons(tyBuiltInTypeClass, @[newTypeS(paramType.kind, c)]) result = addImplicitGeneric(typ) else: for i in 0 .. 0: result = semTypeNode(c, n.sons[length - 1], prev) n.typ = result n.sons[length - 1].typ = result else: result = nil proc semBlockType(c: PContext, n: PNode, prev: PType): PType = inc(c.p.nestedBlockCounter) checkSonsLen(n, 2) openScope(c) if n.sons[0].kind notin {nkEmpty, nkSym}: addDecl(c, newSymS(skLabel, n.sons[0], c)) result = semStmtListType(c, n.sons[1], prev) n.sons[1].typ = result n.typ = result closeScope(c) dec(c.p.nestedBlockCounter) proc semGenericParamInInvocation(c: PContext, n: PNode): PType = result = semTypeNode(c, n, nil) proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: localError(n.info, "cannot instantiate the '$1' $2" % [s.name.s, ($s.kind).substr(2).toLower]) return newOrPrevType(tyError, prev, c) var t = s.typ if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: t = t.base result = newOrPrevType(tyGenericInvocation, prev, c) addSonSkipIntLit(result, t) template addToResult(typ) = if typ.isNil: internalAssert false rawAddSon(result, typ) else: addSonSkipIntLit(result, typ) if t.kind == tyForward: for i in countup(1, sonsLen(n)-1): var elem = semGenericParamInInvocation(c, n.sons[i]) addToResult(elem) return elif t.kind != tyGenericBody: # we likely got code of the form TypeA[TypeB] where TypeA is # not generic. localError(n.info, errNoGenericParamsAllowedForX, s.name.s) return newOrPrevType(tyError, prev, c) else: var m = newCandidate(c, t) m.isNoCall = true matches(c, n, copyTree(n), m) if m.state != csMatch and not m.typedescMatched: let err = "cannot instantiate " & typeToString(t) & "\n" & "got: (" & describeArgs(c, n) & ")\n" & "but expected: (" & describeArgs(c, t.n, 0) & ")" localError(n.info, errGenerated, err) return newOrPrevType(tyError, prev, c) var isConcrete = true for i in 1 ..