# # # The Nimrod 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(tyTypeClass, c) result.addSonSkipIntLit(newTypeS(k, c)) proc semEnum(c: PContext, n: PNode, prev: PType): PType = if n.sonsLen == 0: return newConstraint(c, tyEnum) 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: e = newSymS(skEnumField, n.sons[i], c) else: illFormedAst(n) 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)) 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(considerAcc(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 sonsLen(n) == 1: result = newOrPrevType(kind, prev, c) var base = semTypeNode(c, n.sons[0], nil) addSonSkipIntLit(result, base) else: result = newConstraint(c, kind) 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 sonsLen(n) == 1: result = newOrPrevType(tyDistinct, prev, c) addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) else: result = newConstraint(c, tyDistinct) 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 a = semConstExpr(c, n[1]) var b = semConstExpr(c, n[2]) if not sameType(a.typ, b.typ): LocalError(n.info, errPureTypeMismatch) elif a.typ.kind notin {tyInt..tyInt64,tyEnum,tyBool,tyChar, tyFloat..tyFloat128,tyUInt8..tyUInt32}: LocalError(n.info, errOrdinalTypeExpected) elif enumHasHoles(a.typ): LocalError(n.info, errEnumXHas
discard """
  output: '''0
1
2
3
4
5
6
7
8
9
10
5 5
7 7
9 9
0
0
0
0
1
2
70'''
"""

when true:
  proc main() =
    let
      lo=0
      hi=10

    iterator itA(): int =
      for x in lo..hi:
        yield x

    for x in itA():
      echo x

    var y: int

    iterator itB(): int =
      while y <= hi:
        yield y
        inc y

    y = 5
    for x in itB():
      echo x, " ", y
      inc y

  main()


iterator infinite(): int {.closure.} =
  var i = 0
  while true:
    yield i
    inc i

iterator take[T](it: iterator (): T, numToTake: int): T {.closure.} =
  var i = 0
  for x in it():
    if i >= numToTake:
      break
    yield x
    inc i

# gives wrong reasult (3 times 0)
for x in infinite.take(3):
  echo x

# does what we want
let inf = infinite
for x in inf.take(3):
  echo x

# bug #3583
proc foo(f: (iterator(): int)) =
  for i in f(): echo i

let fIt = iterator(): int = yield 70
foo fIt
tedFields(c, check, pos, obj.sons[0]) addInheritedFieldsAux(c, check, pos, obj.n) proc skipGenericInvokation(t: PType): PType {.inline.} = result = t if result.kind == tyGenericInvokation: result = result.sons[0] if result.kind == tyGenericBody: result = lastSon(result) 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 = skipTypes(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) var concreteBase = skipGenericInvokation(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 addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = if kind == skMacro and param.typ.kind != tyTypeDesc: # within a macro, every param has the type PNimrodNode! # and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}: let nn = getSysSym"PNimrodNode" var a = copySym(param) a.typ = nn.typ if sfGenSym notin a.flags: addDecl(c, a) else: if sfGenSym notin param.flags: addDecl(c, param) 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 = addImplicitGeneric(newTypeS(tyGenericParam, c)) else: # proc(a: expr{string}, b: expr{nkLambda}) # overload on compile time values and AST trees result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons)) of tyTypeDesc: if tfUnresolved notin paramType.flags: result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) 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: # 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 .. 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 semGenericParamInInvokation(c: PContext, n: PNode): PType = # XXX hack 1022 for generics ... would have been nice if the compiler had # been designed with them in mind from start ... when false: if n.kind == nkSym: # for generics we need to lookup the type var again: var s = searchInScopes(c, n.sym.name) if s != nil: if s.kind == skType and s.typ != nil: var t = n.sym.typ echo "came here" return t else: echo "s is crap:" debug(s) else: echo "s is nil!!!!" result = semTypeNode(c, n, nil) proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = result = newOrPrevType(tyGenericInvokation, prev, c) addSonSkipIntLit(result, s.typ) template addToResult(typ) = if typ.isNil: InternalAssert false rawAddSon(result, typ) else: addSonSkipIntLit(result, typ) if s.typ == nil: LocalError(n.info, errCannotInstantiateX, s.name.s) return newOrPrevType(tyError, prev, c) elif s.typ.kind == tyForward: for i in countup(1, sonsLen(n)-1): var elem = semGenericParamInInvokation(c, n.sons[i]) addToResult(elem) else: internalAssert s.typ.kind == tyGenericBody var m = newCandidate(s, n) matches(c, n, copyTree(n), m) if m.state != csMatch: var err = "cannot instantiate " & typeToString(s.typ) & "\n" & "got: (" & describeArgs(c, n) & ")\n" & "but expected: (" & describeArgs(c, s.typ.n, 0) & ")" LocalError(n.info, errGenerated, err) return newOrPrevType(tyError, prev, c) var isConcrete = true for i in 1 .. 0 InternalAssert prev == nil result = s.typ.sons[0] elif prev == nil: result = s.typ else: assignType(prev, s.typ) # bugfix: keep the fresh id for aliases to integral types: if s.typ.kind notin {tyBool, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64}: prev.id = s.typ.id result = prev of nkSym: if n.sym.kind == skType and n.sym.typ != nil: var t = n.sym.typ if prev == nil: result = t else: assignType(prev, t) result = prev markUsed(n, n.sym) else: if n.sym.kind != skError: LocalError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) of nkObjectTy: result = semObjectNode(c, n, prev) of nkTupleTy: result = semTuple(c, n, prev) of nkRefTy: result = semAnyRef(c, n, tyRef, prev) of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkVarTy: result = semVarType(c, n, prev) of nkDistinctTy: result = semDistinct(c, n, prev) of nkProcTy, nkIteratorTy: if n.sonsLen == 0: result = newConstraint(c, tyProc) else: checkSonsLen(n, 2) openScope(c) result = semProcTypeNode(c, n.sons[0], nil, prev, skProc) # dummy symbol for `pragma`: var s = newSymS(skProc, newIdentNode(getIdent("dummy"), n.info), c) s.typ = result if n.sons[1].kind == nkEmpty or n.sons[1].len == 0: if result.callConv == ccDefault: result.callConv = ccClosure #Message(n.info, warnImplicitClosure, renderTree(n)) else: pragma(c, s, n.sons[1], procTypePragmas) when useEffectSystem: SetEffectsForProcType(result, n.sons[1]) closeScope(c) if n.kind == nkIteratorTy: result.flags.incl(tfIterator) result.callConv = ccClosure of nkEnumTy: result = semEnum(c, n, prev) of nkType: result = n.typ of nkStmtListType: result = semStmtListType(c, n, prev) of nkBlockType: result = semBlockType(c, n, prev) of nkSharedTy: checkSonsLen(n, 1) result = semTypeNode(c, n.sons[0], prev) result = freshType(result, prev) result.flags.incl(tfShared) else: LocalError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) proc setMagicType(m: PSym, kind: TTypeKind, size: int) = m.typ.kind = kind m.typ.align = size m.typ.size = size proc processMagicType(c: PContext, m: PSym) = case m.magic of mInt: setMagicType(m, tyInt, intSize) of mInt8: setMagicType(m, tyInt8, 1) of mInt16: setMagicType(m, tyInt16, 2) of mInt32: setMagicType(m, tyInt32, 4) of mInt64: setMagicType(m, tyInt64, 8) of mUInt: setMagicType(m, tyUInt, intSize) of mUInt8: setMagicType(m, tyUInt8, 1) of mUInt16: setMagicType(m, tyUInt16, 2) of mUInt32: setMagicType(m, tyUInt32, 4) of mUInt64: setMagicType(m, tyUInt64, 8) of mFloat: setMagicType(m, tyFloat, floatSize) of mFloat32: setMagicType(m, tyFloat32, 4) of mFloat64: setMagicType(m, tyFloat64, 8) of mFloat128: setMagicType(m, tyFloat128, 16) of mBool: setMagicType(m, tyBool, 1) of mChar: setMagicType(m, tyChar, 1) of mString: setMagicType(m, tyString, ptrSize) rawAddSon(m.typ, getSysType(tyChar)) of mCstring: setMagicType(m, tyCString, ptrSize) rawAddSon(m.typ, getSysType(tyChar)) of mPointer: setMagicType(m, tyPointer, ptrSize) of mEmptySet: setMagicType(m, tySet, 1) rawAddSon(m.typ, newTypeS(tyEmpty, c)) of mIntSetBaseType: setMagicType(m, tyRange, intSize) of mNil: setMagicType(m, tyNil, ptrSize) of mExpr: setMagicType(m, tyExpr, 0) of mStmt: setMagicType(m, tyStmt, 0) of mTypeDesc: setMagicType(m, tyTypeDesc, 0) of mVoidType: setMagicType(m, tyEmpty, 0) of mArray: setMagicType(m, tyArray, 0) of mOpenArray: setMagicType(m, tyOpenArray, 0) of mVarargs: setMagicType(m, tyVarargs, 0) of mRange: setMagicType(m, tyRange, 0) of mSet: setMagicType(m, tySet, 0) of mSeq: setMagicType(m, tySequence, 0) of mOrdinal: setMagicType(m, tyOrdinal, 0) of mPNimrodNode: nil else: LocalError(m.info, errTypeExpected) proc semGenericConstraints(c: PContext, x: PType): PType = if x.kind in StructuralEquivTypes and ( sonsLen(x) == 0 or x.sons[0].kind in {tyGenericParam, tyEmpty}): result = newConstraint(c, x.kind) else: result = newTypeWithSons(c, tyGenericParam, @[x]) proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = result = copyNode(n) if n.kind != nkGenericParams: illFormedAst(n) return for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if a.kind != nkIdentDefs: illFormedAst(n) let L = a.len var def = a{-1} let constraint = a{-2} var typ: PType if constraint.kind != nkEmpty: typ = semTypeNode(c, constraint, nil) if typ.kind != tyExpr or typ.len == 0: if typ.len == 0 and typ.kind == tyTypeDesc: typ = newTypeS(tyGenericParam, c) else: typ = semGenericConstraints(c, typ) if def.kind != nkEmpty: def = semConstExpr(c, def) if typ == nil: if def.typ.kind != tyTypeDesc: typ = newTypeWithSons(c, tyExpr, @[def.typ]) else: if not containsGenericType(def.typ): def = fitNode(c, typ, def) if typ == nil: typ = newTypeS(tyGenericParam, c) for j in countup(0, L-3): let finalType = if j == 0: typ else: copyType(typ, typ.owner, false) # it's important the we create an unique # type for each generic param. the index # of the parameter will be stored in the # attached symbol. var s = case finalType.kind of tyExpr: newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) else: newSymG(skType, a.sons[j], c).linkTo(finalType) if def.kind != nkEmpty: s.ast = def if father != nil: addSonSkipIntLit(father, s.typ) s.position = result.len addSon(result, newSymNode(s)) if sfGenSym notin s.flags: addDecl(c, s)