# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This include file implements the semantic checking for magics. # included from sem.nim proc semAddrArg(c: PContext; n: PNode; isUnsafeAddr = false): PNode = let x = semExprWithType(c, n) if x.kind == nkSym: x.sym.flags.incl(sfAddrTaken) if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: # Do not suggest the use of unsafeAddr if this expression already is a # unsafeAddr if isUnsafeAddr: localError(c.config, n.info, errExprHasNoAddress) else: localError(c.config, n.info, errExprHasNoAddress & "; maybe use 'unsafeAddr'") result = x proc semTypeOf(c: PContext; n: PNode): PNode = var m = BiggestInt 1 # typeOfIter if n.len == 3: let mode = semConstExpr(c, n[2]) if mode.kind != nkIntLit: localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time") else: m = mode.intVal result = newNodeI(nkTypeOfExpr, n.info) let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {}) result.add typExpr result.typ = makeTypeDesc(c, typExpr.typ) type SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode proc skipAddr(n: PNode): PNode {.inline.} = (if n.kind == nkHiddenAddr: n.sons[0] else: n) proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = result = newNodeI(nkBracketExpr, n.info) for i in 1..= 2: let preferStr = traitCall.sons[2].strVal prefer = parseEnum[TPreferedDesc](preferStr) result = newStrNode(nkStrLit, operand.typeToString(prefer)) result.typ = newType(tyString, context) result.info = traitCall.info of "name", "$": result = newStrNode(nkStrLit, operand.typeToString(preferTypeName)) result.typ = newType(tyString, context) result.info = traitCall.info of "arity": result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc)) result.typ = newType(tyInt, context) result.info = traitCall.info of "genericHead": var res = uninstantiate(operand) if res == operand and res.kind notin tyMagicGenerics: localError(c.config, traitCall.info, "genericHead expects a generic type. The given type was " & typeToString(operand)) return newType(tyError, context).toNode(traitCall.info) result = res.base.toNode(traitCall.info) of "stripGenericParams": result = uninstantiate(operand).toNode(traitCall.info) of "supportsCopyMem": let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred}) let complexObj = containsGarbageCollectedRef(t) or hasDestructor(t) result = newIntNodeT(ord(not complexObj), traitCall, c.graph) else: localError(c.config, traitCall.info, "unknown trait: " & s) result = newNodeI(nkEmpty, traitCall.info) proc semTypeTraits(c: PContext, n: PNode): PNode = checkMinSonsLen(n, 2, c.config) let t = n.sons[1].typ internalAssert c.config, t != nil and t.kind == tyTypeDesc if t.len > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) result = evalTypeTrait(c, n, t, getCurrOwner(c)) else: # a typedesc variable, pass unmodified to evals result = n proc semOrd(c: PContext, n: PNode): PNode = result = n let parType = n.sons[1].typ if isOrdinalType(parType, allowEnumWithHoles=true): discard elif parType.kind == tySet: let a = toInt64(firstOrd(c.config, parType)) let b = toInt64(lastOrd(c.config, parType)) result.typ = makeRangeType(c, a, b, n.info) else: localError(c.config, n.info, errOrdinalTypeExpected) result.typ = errorType(c) proc semBindSym(c: PContext, n: PNode): PNode = result = copyNode(n) result.add(n.sons[0]) let sl = semConstExpr(c, n.sons[1]) if sl.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: localError(c.config, n.sons[1].info, errStringLiteralExpected) return errorNode(c, n) let isMixin = semConstExpr(c, n.sons[2]) if isMixin.kind != nkIntLit or isMixin.intVal < 0 or isMixin.intVal > high(TSymChoiceRule).int: localError(c.config, n.sons[2].info, errConstExprExpected) return errorNode(c, n) let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info) let s = qualifiedLookUp(c, id, {checkUndeclared}) if s != nil: # we need to mark all symbols: var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal)) if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc): # inside regular code, bindSym resolves to the sym-choice # nodes (see tinspectsymbol) return sc result.add(sc) else: errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal) proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode = if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}: localError(c.config, info.info, errStringOrIdentNodeExpected) return errorNode(c, n) if isMixin < 0 or isMixin > high(TSymChoiceRule).int: localError(c.config, info.info, errConstExprExpected) return errorNode(c, n) let id = if n.kind == nkIdent: n else: newIdentNode(getIdent(c.cache, n.strVal), info.info) let tmpScope = c.currentScope c.currentScope = scope let s = qualifiedLookUp(c, id, {checkUndeclared}) if s != nil: # we need to mark all symbols: result = symChoice(c, id, s, TSymChoiceRule(isMixin)) else: errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s else: n.strVal) c.currentScope = tmpScope proc semDynamicBindSym(c: PContext, n: PNode): PNode = # inside regular code, bindSym resolves to the sym-choice # nodes (see tinspectsymbol) if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc): return semBindSym(c, n) if c.graph.vm.isNil: setupGlobalCtx(c.module, c.graph) let vm = PCtx c.graph.vm # cache the current scope to # prevent it lost into oblivion scope = c.currentScope # cannot use this # vm.config.features.incl dynamicBindSym proc bindSymWrapper(a: VmArgs) = # capture PContext and currentScope # param description: # 0. ident, a string literal / computed string / or ident node # 1. bindSym rule # 2. info node a.setResult opBindSym(c, scope, a.getNode(0), a.getInt(1).int, a.getNode(2)) let # altough we use VM callback here, it is not # executed like 'normal' VM callback idx = vm.registerCallback("bindSymImpl", bindSymWrapper) # dummy node to carry idx information to VM idxNode = newIntTypeNode(idx, c.graph.getSysType(TLineInfo(), tyInt)) result = copyNode(n) for x in n: result.add x result.add n # info node result.add idxNode proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode proc semOf(c: PContext, n: PNode): PNode = if len(n) == 3: n.sons[1] = semExprWithType(c, n.sons[1]) n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType}) #restoreOldStyleType(n.sons[1]) #restoreOldStyleType(n.sons[2]) let a = skipTypes(n.sons[1].typ, abstractPtrs) let b = skipTypes(n.sons[2].typ, abstractPtrs) let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc}) let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc}) if x.kind == tyTypeDesc or y.kind != tyTypeDesc: localError(c.config, n.info, "'of' takes object types") elif b.kind != tyObject or a.kind != tyObject: localError(c.config, n.info, "'of' takes object types") else: let diff = inheritanceDiff(a, b) # | returns: 0 iff `a` == `b` # | returns: -x iff `a` is the x'th direct superclass of `b` # | returns: +x iff `a` is the x'th direct subclass of `b` # | returns: `maxint` iff `a` and `b` are not compatible at all if diff <= 0: # optimize to true: message(c.config, n.info, hintConditionAlwaysTrue, renderTree(n)) result = newIntNode(nkIntLit, 1) result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) return result elif diff == high(int): if commonSuperclass(a, b) == nil: localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a)) else: message(c.config, n.info, hintConditionAlwaysFalse, renderTree(n)) result = newIntNode(nkIntLit, 0) result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) else: localError(c.config, n.info, "'of' takes 2 arguments") n.typ = getSysType(c.graph, n.info, tyBool) result = n proc semUnown(c: PContext; n: PNode): PNode = proc unownedType(c: PContext; t: PType): PType = case t.kind of tyTuple: var elems = newSeq[PType](t.len) var someChange = false for i in 0..