diff options
Diffstat (limited to 'rod/semexprs.nim')
-rwxr-xr-x | rod/semexprs.nim | 1113 |
1 files changed, 1113 insertions, 0 deletions
diff --git a/rod/semexprs.nim b/rod/semexprs.nim new file mode 100755 index 000000000..34db72138 --- /dev/null +++ b/rod/semexprs.nim @@ -0,0 +1,1113 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2009 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# +# this module does the semantic checking for expressions + +proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck: bool = true): PNode = + markUsed(n, s) + pushInfoContext(n.info) + result = evalTemplate(c, n, s) + if semCheck: result = semAfterMacroCall(c, result, s) + popInfoContext() + +proc semDotExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode +proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = + var d: PNode + result = semExpr(c, n, flags) + if result == nil: InternalError("semExprWithType") + if (result.typ == nil): + liMessage(n.info, errExprXHasNoType, renderTree(result, {renderNoComments})) + if result.typ.kind == tyVar: + d = newNodeIT(nkHiddenDeref, result.info, result.typ.sons[0]) + addSon(d, result) + result = d + +proc checkConversionBetweenObjects(info: TLineInfo, castDest, src: PType) = + var diff: int + diff = inheritanceDiff(castDest, src) + if diff == high(int): + liMessage(info, errGenerated, `%`(MsgKindToString(errIllegalConvFromXtoY), [ + typeToString(src), typeToString(castDest)])) + +proc checkConvertible(info: TLineInfo, castDest, src: PType) = + const + IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyFloat128} + var d, s: PType + if sameType(castDest, src): + # don't annoy conversions that may be needed on another processor: + if not (castDest.kind in {tyInt..tyFloat128, tyNil}): + liMessage(info, hintConvFromXtoItselfNotNeeded, typeToString(castDest)) + return + d = skipTypes(castDest, abstractVar) + s = skipTypes(src, abstractVar) + while (d != nil) and (d.Kind in {tyPtr, tyRef}) and (d.Kind == s.Kind): + d = base(d) + s = base(s) + if d == nil: + liMessage(info, errGenerated, `%`(msgKindToString(errIllegalConvFromXtoY), [ + typeToString(src), typeToString(castDest)])) + if (d.Kind == tyObject) and (s.Kind == tyObject): + checkConversionBetweenObjects(info, d, s) + elif (skipTypes(castDest, abstractVarRange).Kind in IntegralTypes) and + (skipTypes(src, abstractVarRange).Kind in IntegralTypes): + # accept conversion between intregral types + else: + # we use d, s here to speed up that operation a bit: + case cmpTypes(d, s) + of isNone, isGeneric: + if not equalOrDistinctOf(castDest, src) and + not equalOrDistinctOf(src, castDest): + liMessage(info, errGenerated, `%`( + MsgKindToString(errIllegalConvFromXtoY), + [typeToString(src), typeToString(castDest)])) + else: + nil + +proc isCastable(dst, src: PType): bool = + #const + # castableTypeKinds = {@set}[tyInt, tyPtr, tyRef, tyCstring, tyString, + # tySequence, tyPointer, tyNil, tyOpenArray, + # tyProc, tySet, tyEnum, tyBool, tyChar]; + var ds, ss: biggestInt + # this is very unrestrictive; cast is allowed if castDest.size >= src.size + ds = computeSize(dst) + ss = computeSize(src) + if ds < 0: + result = false + elif ss < 0: + result = false + else: + result = (ds >= ss) or + (skipTypes(dst, abstractInst).kind in {tyInt..tyFloat128}) or + (skipTypes(src, abstractInst).kind in {tyInt..tyFloat128}) + +proc semConv(c: PContext, n: PNode, s: PSym): PNode = + var op: PNode + if sonsLen(n) != 2: liMessage(n.info, errConvNeedsOneArg) + result = newNodeI(nkConv, n.info) + result.typ = semTypeNode(c, n.sons[0], nil) + addSon(result, copyTree(n.sons[0])) + addSon(result, semExprWithType(c, n.sons[1])) + op = result.sons[1] + if op.kind != nkSymChoice: + checkConvertible(result.info, result.typ, op.typ) + else: + for i in countup(0, sonsLen(op) - 1): + if sameType(result.typ, op.sons[i].typ): + markUsed(n, op.sons[i].sym) + return op.sons[i] + liMessage(n.info, errUseQualifier, op.sons[0].sym.name.s) + +proc semCast(c: PContext, n: PNode): PNode = + if optSafeCode in gGlobalOptions: liMessage(n.info, errCastNotInSafeMode) + incl(c.p.owner.flags, sfSideEffect) + checkSonsLen(n, 2) + result = newNodeI(nkCast, n.info) + result.typ = semTypeNode(c, n.sons[0], nil) + addSon(result, copyTree(n.sons[0])) + addSon(result, semExprWithType(c, n.sons[1])) + if not isCastable(result.typ, result.sons[1].Typ): + liMessage(result.info, errExprCannotBeCastedToX, typeToString(result.Typ)) + +proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = + const + opToStr: array[mLow..mHigh, string] = ["low", "high"] + var typ: PType + if sonsLen(n) != 2: + liMessage(n.info, errXExpectsTypeOrValue, opToStr[m]) + else: + n.sons[1] = semExprWithType(c, n.sons[1], {efAllowType}) + typ = skipTypes(n.sons[1].typ, abstractVarRange) + case typ.Kind + of tySequence, tyString, tyOpenArray: + n.typ = getSysType(tyInt) + of tyArrayConstr, tyArray: + n.typ = n.sons[1].typ.sons[0] # indextype + of tyInt..tyInt64, tyChar, tyBool, tyEnum: + n.typ = n.sons[1].typ + else: liMessage(n.info, errInvalidArgForX, opToStr[m]) + result = n + +proc semSizeof(c: PContext, n: PNode): PNode = + if sonsLen(n) != 2: liMessage(n.info, errXExpectsTypeOrValue, "sizeof") + else: n.sons[1] = semExprWithType(c, n.sons[1], {efAllowType}) + n.typ = getSysType(tyInt) + result = n + +proc semIs(c: PContext, n: PNode): PNode = + var a, b: PType + if sonsLen(n) == 3: + n.sons[1] = semExprWithType(c, n.sons[1], {efAllowType}) + n.sons[2] = semExprWithType(c, n.sons[2], {efAllowType}) + a = n.sons[1].typ + b = n.sons[2].typ + if (b.kind != tyObject) or (a.kind != tyObject): + liMessage(n.info, errIsExpectsObjectTypes) + while (b != nil) and (b.id != a.id): b = b.sons[0] + if b == nil: liMessage(n.info, errXcanNeverBeOfThisSubtype, typeToString(a)) + n.typ = getSysType(tyBool) + else: + liMessage(n.info, errIsExpectsTwoArguments) + result = n + +proc semOpAux(c: PContext, n: PNode) = + var + a: PNode + info: TLineInfo + for i in countup(1, sonsLen(n) - 1): + a = n.sons[i] + if a.kind == nkExprEqExpr: + checkSonsLen(a, 2) + info = a.sons[0].info + a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info) + a.sons[1] = semExprWithType(c, a.sons[1]) + a.typ = a.sons[1].typ + else: + n.sons[i] = semExprWithType(c, a) + +proc overloadedCallOpr(c: PContext, n: PNode): PNode = + var par: PIdent + # quick check if there is *any* () operator overloaded: + par = getIdent("()") + if SymtabGet(c.Tab, par) == nil: + result = nil + else: + result = newNodeI(nkCall, n.info) + addSon(result, newIdentNode(par, n.info)) + for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i]) + result = semExpr(c, result) + +proc changeType(n: PNode, newType: PType) = + var + f: PSym + a, m: PNode + case n.kind + of nkCurly, nkBracket: + for i in countup(0, sonsLen(n) - 1): changeType(n.sons[i], elemType(newType)) + of nkPar: + if newType.kind != tyTuple: + InternalError(n.info, "changeType: no tuple type for constructor") + if newType.n == nil: InternalError(n.info, "changeType: no tuple fields") + if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr): + for i in countup(0, sonsLen(n) - 1): + m = n.sons[i].sons[0] + if m.kind != nkSym: + internalError(m.info, "changeType(): invalid tuple constr") + f = getSymFromList(newType.n, m.sym.name) + if f == nil: internalError(m.info, "changeType(): invalid identifier") + changeType(n.sons[i].sons[1], f.typ) + else: + for i in countup(0, sonsLen(n) - 1): + m = n.sons[i] + a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) + addSon(a, newSymNode(newType.n.sons[i].sym)) + addSon(a, m) + changeType(m, newType.sons[i]) + n.sons[i] = a + else: + nil + n.typ = newType + +proc semArrayConstr(c: PContext, n: PNode): PNode = + var typ: PType + result = newNodeI(nkBracket, n.info) + result.typ = newTypeS(tyArrayConstr, c) + addSon(result.typ, nil) # index type + if sonsLen(n) == 0: + addSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype! + else: + addSon(result, semExprWithType(c, n.sons[0])) + typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) + for i in countup(1, sonsLen(n) - 1): + n.sons[i] = semExprWithType(c, n.sons[i]) + addSon(result, fitNode(c, typ, n.sons[i])) + addSon(result.typ, typ) + result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info) + +const + ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, + tyArrayConstr, tyTuple, tySet} + +proc fixAbstractType(c: PContext, n: PNode) = + var + s: PType + it: PNode + for i in countup(1, sonsLen(n) - 1): + it = n.sons[i] + case it.kind + of nkHiddenStdConv, nkHiddenSubConv: + if it.sons[1].kind == nkBracket: + it.sons[1] = semArrayConstr(c, it.sons[1]) + if skipTypes(it.typ, abstractVar).kind == tyOpenArray: + s = skipTypes(it.sons[1].typ, abstractVar) + if (s.kind == tyArrayConstr) and (s.sons[1].kind == tyEmpty): + s = copyType(s, getCurrOwner(), false) + skipTypes(s, abstractVar).sons[1] = elemType( + skipTypes(it.typ, abstractVar)) + it.sons[1].typ = s + elif skipTypes(it.sons[1].typ, abstractVar).kind in + {tyNil, tyArrayConstr, tyTuple, tySet}: + s = skipTypes(it.typ, abstractVar) + changeType(it.sons[1], s) + n.sons[i] = it.sons[1] + of nkBracket: + # an implicitely constructed array (passed to an open array): + n.sons[i] = semArrayConstr(c, it) + else: + if (it.typ == nil): + InternalError(it.info, "fixAbstractType: " & renderTree(it)) + +proc skipObjConv(n: PNode): PNode = + case n.kind + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if skipTypes(n.sons[1].typ, abstractPtrs).kind in {tyTuple, tyObject}: + result = n.sons[1] + else: + result = n + of nkObjUpConv, nkObjDownConv: + result = n.sons[0] + else: result = n + +type + TAssignableResult = enum + arNone, # no l-value and no discriminant + arLValue, # is an l-value + arDiscriminant # is a discriminant + +proc isAssignable(n: PNode): TAssignableResult = + result = arNone + case n.kind + of nkSym: + if (n.sym.kind in {skVar, skTemp}): result = arLValue + of nkDotExpr: + checkMinSonsLen(n, 1) + if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}: + result = arLValue + else: + result = isAssignable(n.sons[0]) + if (result == arLValue) and (sfDiscriminant in n.sons[1].sym.flags): + result = arDiscriminant + of nkBracketExpr: + checkMinSonsLen(n, 1) + if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}: + result = arLValue + else: + result = isAssignable(n.sons[0]) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + # Object and tuple conversions are still addressable, so we skip them + #if skipPtrsGeneric(n.sons[1].typ).kind in [tyOpenArray, + # tyTuple, tyObject] then + if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}: + result = isAssignable(n.sons[1]) + of nkHiddenDeref, nkDerefExpr: + result = arLValue + of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: + result = isAssignable(n.sons[0]) + else: + nil + +proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = + if n.kind == nkHiddenDeref: + checkSonsLen(n, 1) + result = n.sons[0] + else: + result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) + addSon(result, n) + if isAssignable(n) != arLValue: + liMessage(n.info, errVarForOutParamNeeded) + +proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = + result = n + case n.kind + of nkSym: + if skipTypes(n.sym.typ, abstractInst).kind != tyVar: + incl(n.sym.flags, sfAddrTaken) + result = newHiddenAddrTaken(c, n) + of nkDotExpr: + checkSonsLen(n, 2) + if n.sons[1].kind != nkSym: internalError(n.info, "analyseIfAddressTaken") + if skipTypes(n.sons[1].sym.typ, abstractInst).kind != tyVar: + incl(n.sons[1].sym.flags, sfAddrTaken) + result = newHiddenAddrTaken(c, n) + of nkBracketExpr: + checkMinSonsLen(n, 1) + if skipTypes(n.sons[0].typ, abstractInst).kind != tyVar: + if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken) + result = newHiddenAddrTaken(c, n) + else: + result = newHiddenAddrTaken(c, n) # BUGFIX! + +proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = + const + FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, + mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, + mAppendSeqElem, mNewSeq} + var t: PType + checkMinSonsLen(n, 1) + t = n.sons[0].typ + if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic in FakeVarParams): + return + for i in countup(1, sonsLen(n) - 1): + if (i < sonsLen(t)) and + (skipTypes(t.sons[i], abstractInst).kind == tyVar): + n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) + +proc semDirectCallAnalyseEffects(c: PContext, n: PNode, flags: TExprFlags): PNode = + var callee: PSym + if not (efWantIterator in flags): + result = semDirectCall(c, n, {skProc, skMethod, skConverter}) + else: + result = semDirectCall(c, n, {skIterator}) + if result != nil: + if result.sons[0].kind != nkSym: + InternalError("semDirectCallAnalyseEffects") + callee = result.sons[0].sym + if (callee.kind == skIterator) and (callee.id == c.p.owner.id): + liMessage(n.info, errRecursiveDependencyX, callee.name.s) + if not (sfNoSideEffect in callee.flags): + if (sfForward in callee.flags) or + ({sfImportc, sfSideEffect} * callee.flags != {}): + incl(c.p.owner.flags, sfSideEffect) + +proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = + var + m: TCandidate + msg: string + prc: PNode + t: PType + result = nil + prc = n.sons[0] + checkMinSonsLen(n, 1) + if n.sons[0].kind == nkDotExpr: + checkSonsLen(n.sons[0], 2) + n.sons[0] = semDotExpr(c, n.sons[0]) + if n.sons[0].kind == nkDotCall: + # it is a static call! + result = n.sons[0] + result.kind = nkCall + for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) + return semExpr(c, result, flags) + else: + n.sons[0] = semExpr(c, n.sons[0]) + semOpAux(c, n) + if (n.sons[0].typ != nil): t = skipTypes(n.sons[0].typ, abstractInst) + else: t = nil + if (t != nil) and (t.kind == tyProc): + initCandidate(m, t) + matches(c, n, m) + if m.state != csMatch: + msg = msgKindToString(errTypeMismatch) + for i in countup(1, sonsLen(n) - 1): + if i > 1: add(msg, ", ") + add(msg, typeToString(n.sons[i].typ)) + add(msg, ')' & "\n" & msgKindToString(errButExpected) & "\n" & + typeToString(n.sons[0].typ)) + liMessage(n.Info, errGenerated, msg) + result = nil + else: + result = m.call # we assume that a procedure that calls something indirectly + # has side-effects: + if not (tfNoSideEffect in t.flags): incl(c.p.owner.flags, sfSideEffect) + else: + result = overloadedCallOpr(c, n) # Now that nkSym does not imply an iteration over the proc/iterator space, + # the old ``prc`` (which is likely an nkIdent) has to be restored: + if result == nil: + n.sons[0] = prc + result = semDirectCallAnalyseEffects(c, n, flags) + if result == nil: + liMessage(n.info, errExprXCannotBeCalled, + renderTree(n, {renderNoComments})) + fixAbstractType(c, result) + analyseIfAddressTakenInCall(c, result) + +proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = + # this seems to be a hotspot in the compiler! + semOpAux(c, n) + result = semDirectCallAnalyseEffects(c, n, flags) + if result == nil: + result = overloadedCallOpr(c, n) + if result == nil: liMessage(n.Info, errGenerated, getNotFoundError(c, n)) + fixAbstractType(c, result) + analyseIfAddressTakenInCall(c, result) + +proc semEcho(c: PContext, n: PNode): PNode = + var call, arg: PNode + # this really is a macro + checkMinSonsLen(n, 1) + for i in countup(1, sonsLen(n) - 1): + arg = semExprWithType(c, n.sons[i]) + call = newNodeI(nkCall, arg.info) + addSon(call, newIdentNode(getIdent("$"), n.info)) + addSon(call, arg) + n.sons[i] = semExpr(c, call) + result = n + +proc LookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = + var + m: PSym + ident: PIdent + case n.kind + of nkIdent: + if onlyCurrentScope: + result = SymtabLocalGet(c.tab, n.ident) + else: + result = SymtabGet(c.Tab, n.ident) # no need for stub loading + of nkDotExpr: + result = nil + if onlyCurrentScope: return + checkSonsLen(n, 2) + m = LookupForDefined(c, n.sons[0], onlyCurrentScope) + if (m != nil) and (m.kind == skModule): + if (n.sons[1].kind == nkIdent): + ident = n.sons[1].ident + if m == c.module: + result = StrTableGet(c.tab.stack[ModuleTablePos], ident) + else: + result = StrTableGet(m.tab, ident) + else: + liMessage(n.sons[1].info, errIdentifierExpected, "") + of nkAccQuoted: + checkSonsLen(n, 1) + result = lookupForDefined(c, n.sons[0], onlyCurrentScope) + else: + liMessage(n.info, errIdentifierExpected, renderTree(n)) + result = nil + +proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = + checkSonsLen(n, 2) + result = newIntNode(nkIntLit, 0) # we replace this node by a 'true' or 'false' node + if LookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil: + result.intVal = 1 + elif not onlyCurrentScope and (n.sons[1].kind == nkIdent) and + condsyms.isDefined(n.sons[1].ident): + result.intVal = 1 + result.info = n.info + result.typ = getSysType(tyBool) + +proc setMs(n: PNode, s: PSym): PNode = + result = n + n.sons[0] = newSymNode(s) + n.sons[0].info = n.info + +proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = + # this is a hotspot in the compiler! + result = n + case s.magic # magics that need special treatment + of mDefined: + result = semDefined(c, setMs(n, s), false) + of mDefinedInScope: + result = semDefined(c, setMs(n, s), true) + of mLow: + result = semLowHigh(c, setMs(n, s), mLow) + of mHigh: + result = semLowHigh(c, setMs(n, s), mHigh) + of mSizeOf: + result = semSizeof(c, setMs(n, s)) + of mIs: + result = semIs(c, setMs(n, s)) + of mEcho: + result = semEcho(c, setMs(n, s)) + else: result = semDirectOp(c, n, flags) + +proc isTypeExpr(n: PNode): bool = + case n.kind + of nkType, nkTypeOfExpr: result = true + of nkSym: result = n.sym.kind == skType + else: result = false + +proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, + check: var PNode): PSym = + # transform in a node that contains the runtime check for the + # field, if it is in a case-part... + var s, it, inExpr, notExpr: PNode + result = nil + case r.kind + of nkRecList: + for i in countup(0, sonsLen(r) - 1): + result = lookupInRecordAndBuildCheck(c, n, r.sons[i], field, check) + if result != nil: return + of nkRecCase: + checkMinSonsLen(r, 2) + if (r.sons[0].kind != nkSym): IllFormedAst(r) + result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check) + if result != nil: return + s = newNodeI(nkCurly, r.info) + for i in countup(1, sonsLen(r) - 1): + it = r.sons[i] + case it.kind + of nkOfBranch: + result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check) + if result == nil: + for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j])) + else: + if check == nil: + check = newNodeI(nkCheckedFieldExpr, n.info) + addSon(check, nil) # make space for access node + s = newNodeI(nkCurly, n.info) + for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j])) + inExpr = newNodeI(nkCall, n.info) + addSon(inExpr, newIdentNode(getIdent("in"), n.info)) + addSon(inExpr, copyTree(r.sons[0])) + addSon(inExpr, s) #writeln(output, renderTree(inExpr)); + addSon(check, semExpr(c, inExpr)) + return + of nkElse: + result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check) + if result != nil: + if check == nil: + check = newNodeI(nkCheckedFieldExpr, n.info) + addSon(check, nil) # make space for access node + inExpr = newNodeI(nkCall, n.info) + addSon(inExpr, newIdentNode(getIdent("in"), n.info)) + addSon(inExpr, copyTree(r.sons[0])) + addSon(inExpr, s) + notExpr = newNodeI(nkCall, n.info) + addSon(notExpr, newIdentNode(getIdent("not"), n.info)) + addSon(notExpr, inExpr) + addSon(check, semExpr(c, notExpr)) + return + else: illFormedAst(it) + of nkSym: + if r.sym.name.id == field.id: result = r.sym + else: illFormedAst(n) + +proc makeDeref(n: PNode): PNode = + var + t: PType + a: PNode + t = skipTypes(n.typ, {tyGenericInst}) + result = n + if t.kind == tyVar: + result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) + addSon(result, n) + t = skipTypes(t.sons[0], {tyGenericInst}) + if t.kind in {tyPtr, tyRef}: + a = result + result = newNodeIT(nkDerefExpr, n.info, t.sons[0]) + addSon(result, a) + +proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = + var + f: PSym + ty: PType + i: PIdent + check: PNode + # this is difficult, because the '.' is used in many different contexts + # in Nimrod. We first allow types in the semantic checking. + checkSonsLen(n, 2) + n.sons[0] = semExprWithType(c, n.sons[0], {efAllowType} + flags) + i = considerAcc(n.sons[1]) + ty = n.sons[0].Typ + f = nil + result = nil + if ty.kind == tyEnum: + # look up if the identifier belongs to the enum: + while (ty != nil): + f = getSymFromList(ty.n, i) + if f != nil: break + ty = ty.sons[0] # enum inheritance + if f != nil: + result = newSymNode(f) + result.info = n.info + result.typ = ty + markUsed(n, f) + else: + liMessage(n.sons[1].info, errEnumHasNoValueX, i.s) + return + elif not (efAllowType in flags) and isTypeExpr(n.sons[0]): + liMessage(n.sons[0].info, errATypeHasNoValue) + return + ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef}) + if ty.kind == tyObject: + while true: + check = nil + f = lookupInRecordAndBuildCheck(c, n, ty.n, i, check) #f := lookupInRecord(ty.n, i); + if f != nil: break + if ty.sons[0] == nil: break + ty = skipTypes(ty.sons[0], {tyGenericInst}) + if f != nil: + if ({sfStar, sfMinus} * f.flags != {}) or + (getModule(f).id == c.module.id): + # is the access to a public field or in the same module? + n.sons[0] = makeDeref(n.sons[0]) + n.sons[1] = newSymNode(f) # we now have the correct field + n.typ = f.typ + markUsed(n, f) + if check == nil: + result = n + else: + check.sons[0] = n + check.typ = n.typ + result = check + return + elif ty.kind == tyTuple: + f = getSymFromList(ty.n, i) + if f != nil: + n.sons[0] = makeDeref(n.sons[0]) + n.sons[1] = newSymNode(f) + n.typ = f.typ + result = n + markUsed(n, f) + return + f = SymTabGet(c.tab, i) #if (f <> nil) and (f.kind = skStub) then loadStub(f); + # ``loadStub`` is not correct here as we don't care for ``f`` really + if (f != nil): + # BUGFIX: do not check for (f.kind in [skProc, skMethod, skIterator]) here + result = newNodeI(nkDotCall, n.info) # This special node kind is to merge with the call handler in `semExpr`. + addSon(result, newIdentNode(i, n.info)) + addSon(result, copyTree(n.sons[0])) + else: + liMessage(n.Info, errUndeclaredFieldX, i.s) + +proc whichSliceOpr(n: PNode): string = + if (n.sons[0] == nil): + if (n.sons[1] == nil): result = "[..]" + else: result = "[..$]" + elif (n.sons[1] == nil): + result = "[$..]" + else: + result = "[$..$]" + +proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = + var + arr, indexType: PType + arg: PNode + idx: biggestInt + # check if array type: + checkMinSonsLen(n, 2) + n.sons[0] = semExprWithType(c, n.sons[0], flags - {efAllowType}) + arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) + case arr.kind + of tyArray, tyOpenArray, tyArrayConstr, tySequence, tyString, tyCString: + n.sons[0] = makeDeref(n.sons[0]) + for i in countup(1, sonsLen(n) - 1): + n.sons[i] = semExprWithType(c, n.sons[i], flags - {efAllowType}) + if arr.kind == tyArray: indexType = arr.sons[0] + else: indexType = getSysType(tyInt) + arg = IndexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1]) + if arg != nil: n.sons[1] = arg + else: liMessage(n.info, errIndexTypesDoNotMatch) + result = n + result.typ = elemType(arr) + of tyTuple: + n.sons[0] = makeDeref(n.sons[0]) # [] operator for tuples requires constant expression + n.sons[1] = semConstExpr(c, n.sons[1]) + if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal}).kind in + {tyInt..tyInt64}: + idx = getOrdValue(n.sons[1]) + if (idx >= 0) and (idx < sonsLen(arr)): n.typ = arr.sons[int(idx)] + else: liMessage(n.info, errInvalidIndexValueForTuple) + else: + liMessage(n.info, errIndexTypesDoNotMatch) + result = n + else: + # overloaded [] operator: + result = newNodeI(nkCall, n.info) + if n.sons[1].kind == nkRange: + checkSonsLen(n.sons[1], 2) + addSon(result, newIdentNode(getIdent(whichSliceOpr(n.sons[1])), n.info)) + addSon(result, n.sons[0]) + addSonIfNotNil(result, n.sons[1].sons[0]) + addSonIfNotNil(result, n.sons[1].sons[1]) + else: + addSon(result, newIdentNode(getIdent("[]"), n.info)) + addSon(result, n.sons[0]) + addSon(result, n.sons[1]) + result = semExpr(c, result) + +proc semIfExpr(c: PContext, n: PNode): PNode = + var + typ: PType + it: PNode + result = n + checkSonsLen(n, 2) + typ = nil + for i in countup(0, sonsLen(n) - 1): + it = n.sons[i] + case it.kind + of nkElifExpr: + checkSonsLen(it, 2) + it.sons[0] = semExprWithType(c, it.sons[0]) + checkBool(it.sons[0]) + it.sons[1] = semExprWithType(c, it.sons[1]) + if typ == nil: typ = it.sons[1].typ + else: it.sons[1] = fitNode(c, typ, it.sons[1]) + of nkElseExpr: + checkSonsLen(it, 1) + it.sons[0] = semExprWithType(c, it.sons[0]) + if (typ == nil): InternalError(it.info, "semIfExpr") + it.sons[0] = fitNode(c, typ, it.sons[0]) + else: illFormedAst(n) + result.typ = typ + +proc semSetConstr(c: PContext, n: PNode): PNode = + var + typ: PType + m: PNode + result = newNodeI(nkCurly, n.info) + result.typ = newTypeS(tySet, c) + if sonsLen(n) == 0: + addSon(result.typ, newTypeS(tyEmpty, c)) + else: + # only semantic checking for all elements, later type checking: + typ = nil + for i in countup(0, sonsLen(n) - 1): + if n.sons[i].kind == nkRange: + checkSonsLen(n.sons[i], 2) + n.sons[i].sons[0] = semExprWithType(c, n.sons[i].sons[0]) + n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1]) + if typ == nil: + typ = skipTypes(n.sons[i].sons[0].typ, + {tyGenericInst, tyVar, tyOrdinal}) + n.sons[i].typ = n.sons[i].sons[1].typ # range node needs type too + else: + n.sons[i] = semExprWithType(c, n.sons[i]) + if typ == nil: + typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyOrdinal}) + if not isOrdinalType(typ): + liMessage(n.info, errOrdinalTypeExpected) + return + if lengthOrd(typ) > MaxSetElements: + typ = makeRangeType(c, 0, MaxSetElements - 1, n.info) + addSon(result.typ, typ) + for i in countup(0, sonsLen(n) - 1): + if n.sons[i].kind == nkRange: + m = newNodeI(nkRange, n.sons[i].info) + addSon(m, fitNode(c, typ, n.sons[i].sons[0])) + addSon(m, fitNode(c, typ, n.sons[i].sons[1])) + else: + m = fitNode(c, typ, n.sons[i]) + addSon(result, m) + +type + TParKind = enum + paNone, paSingle, paTupleFields, paTuplePositions + +proc checkPar(n: PNode): TParKind = + var length: int + length = sonsLen(n) + if length == 0: + result = paTuplePositions # () + elif length == 1: + result = paSingle # (expr) + else: + if n.sons[0].kind == nkExprColonExpr: result = paTupleFields + else: result = paTuplePositions + for i in countup(0, length - 1): + if result == paTupleFields: + if (n.sons[i].kind != nkExprColonExpr) or + not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): + liMessage(n.sons[i].info, errNamedExprExpected) + return paNone + else: + if n.sons[i].kind == nkExprColonExpr: + liMessage(n.sons[i].info, errNamedExprNotAllowed) + return paNone + +proc semTupleFieldsConstr(c: PContext, n: PNode): PNode = + var + typ: PType + ids: TIntSet + id: PIdent + f: PSym + result = newNodeI(nkPar, n.info) + typ = newTypeS(tyTuple, c) + typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs + IntSetInit(ids) + for i in countup(0, sonsLen(n) - 1): + if (n.sons[i].kind != nkExprColonExpr) or + not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): + illFormedAst(n.sons[i]) + if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident + else: id = n.sons[i].sons[0].sym.name + if IntSetContainsOrIncl(ids, id.id): + liMessage(n.sons[i].info, errFieldInitTwice, id.s) + n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1]) + f = newSymS(skField, n.sons[i].sons[0], c) + f.typ = n.sons[i].sons[1].typ + addSon(typ, f.typ) + addSon(typ.n, newSymNode(f)) + n.sons[i].sons[0] = newSymNode(f) + addSon(result, n.sons[i]) + result.typ = typ + +proc semTuplePositionsConstr(c: PContext, n: PNode): PNode = + var typ: PType + result = n # we don't modify n, but compute the type: + typ = newTypeS(tyTuple, c) # leave typ.n nil! + for i in countup(0, sonsLen(n) - 1): + n.sons[i] = semExprWithType(c, n.sons[i]) + addSon(typ, n.sons[i].typ) + result.typ = typ + +proc semStmtListExpr(c: PContext, n: PNode): PNode = + var length: int + result = n + checkMinSonsLen(n, 1) + length = sonsLen(n) + for i in countup(0, length - 2): + n.sons[i] = semStmt(c, n.sons[i]) + if length > 0: + n.sons[length - 1] = semExprWithType(c, n.sons[length - 1]) + n.typ = n.sons[length - 1].typ + +proc semBlockExpr(c: PContext, n: PNode): PNode = + result = n + Inc(c.p.nestedBlockCounter) + checkSonsLen(n, 2) + openScope(c.tab) # BUGFIX: label is in the scope of block! + if n.sons[0] != nil: + addDecl(c, newSymS(skLabel, n.sons[0], c)) + n.sons[1] = semStmtListExpr(c, n.sons[1]) + n.typ = n.sons[1].typ + closeScope(c.tab) + Dec(c.p.nestedBlockCounter) + +proc isCallExpr(n: PNode): bool = + result = n.kind in + {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit} + +proc semMacroStmt(c: PContext, n: PNode, semCheck: bool = true): PNode = + var + s: PSym + a: PNode + checkMinSonsLen(n, 2) + if isCallExpr(n.sons[0]): a = n.sons[0].sons[0] + else: a = n.sons[0] + s = qualifiedLookup(c, a, false) + if (s != nil): + case s.kind + of skMacro: + result = semMacroExpr(c, n, s, semCheck) + of skTemplate: + # transform + # nkMacroStmt(nkCall(a...), stmt, b...) + # to + # nkCall(a..., stmt, b...) + result = newNodeI(nkCall, n.info) + addSon(result, a) + if isCallExpr(n.sons[0]): + for i in countup(1, sonsLen(n.sons[0]) - 1): + addSon(result, n.sons[0].sons[i]) + for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) + result = semTemplateExpr(c, result, s, semCheck) + else: liMessage(n.info, errXisNoMacroOrTemplate, s.name.s) + else: + liMessage(n.info, errInvalidExpressionX, renderTree(a, {renderNoComments})) + +proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = + if (s.kind == skType) and not (efAllowType in flags): + liMessage(n.info, errATypeHasNoValue) + case s.kind + of skProc, skMethod, skIterator, skConverter: + if not (sfProcVar in s.flags) and (s.typ.callConv == ccDefault) and + (getModule(s).id != c.module.id): + liMessage(n.info, warnXisPassedToProcVar, s.name.s) # XXX change this to + # errXCannotBePassedToProcVar after version 0.8.2 + # TODO VERSION 0.8.4 + #if (s.magic <> mNone) then + # liMessage(n.info, + # errInvalidContextForBuiltinX, s.name.s); + result = symChoice(c, n, s) + of skConst: + # + # Consider:: + # const x = [] + # proc p(a: openarray[int]) + # proc q(a: openarray[char]) + # p(x) + # q(x) + # + # It is clear that ``[]`` means two totally different things. Thus, we + # copy `x`'s AST into each context, so that the type fixup phase can + # deal with two different ``[]``. + # + markUsed(n, s) + if s.typ.kind in ConstAbstractTypes: + result = copyTree(s.ast) + result.info = n.info + result.typ = s.typ + else: + result = newSymNode(s) + result.info = n.info + of skMacro: + result = semMacroExpr(c, n, s) + of skTemplate: + result = semTemplateExpr(c, n, s) + of skVar: + markUsed(n, s) # if a proc accesses a global variable, it is not side effect free + if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect) + result = newSymNode(s) + result.info = n.info + of skGenericParam: + if s.ast == nil: InternalError(n.info, "no default for") + result = semExpr(c, s.ast) + else: + markUsed(n, s) + result = newSymNode(s) + result.info = n.info + +proc semDotExpr(c: PContext, n: PNode, flags: TExprFlags): PNode = + var s: PSym + s = qualifiedLookup(c, n, true) # check for ambiguity + if s != nil: # this is a test comment; please don't touch it + result = semSym(c, n, s, flags) + else: + result = semFieldAccess(c, n, flags) + +proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = + var + s: PSym + t: PType + result = n + if n == nil: return + if nfSem in n.flags: return + case n.kind # atoms: + of nkIdent: + s = lookUp(c, n) + result = semSym(c, n, s, flags) + of nkSym: + #s := n.sym; + # include(s.flags, sfUsed); + # if (s.kind = skType) and not (efAllowType in flags) then + # liMessage(n.info, errATypeHasNoValue); + # because of the changed symbol binding, this does not mean that we + # don't have to check the symbol for semantics here again! + result = semSym(c, n, n.sym, flags) + of nkEmpty, nkNone: + nil + of nkNilLit: + result.typ = getSysType(tyNil) + of nkType: + if not (efAllowType in flags): liMessage(n.info, errATypeHasNoValue) + n.typ = semTypeNode(c, n, nil) + of nkIntLit: + if result.typ == nil: result.typ = getSysType(tyInt) + of nkInt8Lit: + if result.typ == nil: result.typ = getSysType(tyInt8) + of nkInt16Lit: + if result.typ == nil: result.typ = getSysType(tyInt16) + of nkInt32Lit: + if result.typ == nil: result.typ = getSysType(tyInt32) + of nkInt64Lit: + if result.typ == nil: result.typ = getSysType(tyInt64) + of nkFloatLit: + if result.typ == nil: result.typ = getSysType(tyFloat) + of nkFloat32Lit: + if result.typ == nil: result.typ = getSysType(tyFloat32) + of nkFloat64Lit: + if result.typ == nil: result.typ = getSysType(tyFloat64) + of nkStrLit..nkTripleStrLit: + if result.typ == nil: result.typ = getSysType(tyString) + of nkCharLit: + if result.typ == nil: result.typ = getSysType(tyChar) + of nkDotExpr: + result = semDotExpr(c, n, flags) + if result.kind == nkDotCall: + result.kind = nkCall + result = semExpr(c, result, flags) + of nkBind: + result = semExpr(c, n.sons[0], flags) + of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: + # check if it is an expression macro: + checkMinSonsLen(n, 1) + s = qualifiedLookup(c, n.sons[0], false) + if (s != nil): + case s.kind + of skMacro: + result = semMacroExpr(c, n, s) + of skTemplate: + result = semTemplateExpr(c, n, s) + of skType: + if n.kind != nkCall: + liMessage(n.info, errXisNotCallable, s.name.s) # XXX does this check make any sense? + result = semConv(c, n, s) + of skProc, skMethod, skConverter, skIterator: + if s.magic == mNone: result = semDirectOp(c, n, flags) + else: result = semMagic(c, n, s, flags) + else: + #liMessage(n.info, warnUser, renderTree(n)); + result = semIndirectOp(c, n, flags) + elif n.sons[0].kind == nkSymChoice: + result = semDirectOp(c, n, flags) + else: + result = semIndirectOp(c, n, flags) + of nkMacroStmt: + result = semMacroStmt(c, n) + of nkBracketExpr: + checkMinSonsLen(n, 1) + s = qualifiedLookup(c, n.sons[0], false) + if (s != nil) and (s.kind in {skProc, skMethod, skConverter, skIterator}): + # type parameters: partial generic specialization + # XXX: too implement! + internalError(n.info, "explicit generic instantation not implemented") + result = partialSpecialization(c, n, s) + else: + result = semArrayAccess(c, n, flags) + of nkPragmaExpr: + # which pragmas are allowed for expressions? `likely`, `unlikely` + internalError(n.info, "semExpr() to implement") # XXX: to implement + of nkPar: + case checkPar(n) + of paNone: result = nil + of paTuplePositions: result = semTuplePositionsConstr(c, n) + of paTupleFields: result = semTupleFieldsConstr(c, n) + of paSingle: result = semExpr(c, n.sons[0]) + of nkCurly: + result = semSetConstr(c, n) + of nkBracket: + result = semArrayConstr(c, n) + of nkLambda: + result = semLambda(c, n) + of nkDerefExpr: + checkSonsLen(n, 1) + n.sons[0] = semExprWithType(c, n.sons[0]) + result = n + t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar}) + case t.kind + of tyRef, tyPtr: n.typ = t.sons[0] + else: liMessage(n.sons[0].info, errCircumNeedsPointer) + result = n + of nkAddr: + result = n + checkSonsLen(n, 1) + n.sons[0] = semExprWithType(c, n.sons[0]) + if isAssignable(n.sons[0]) != arLValue: + liMessage(n.info, errExprHasNoAddress) + n.typ = makePtrType(c, n.sons[0].typ) + of nkHiddenAddr, nkHiddenDeref: + checkSonsLen(n, 1) + n.sons[0] = semExpr(c, n.sons[0], flags) + of nkCast: + result = semCast(c, n) + of nkAccQuoted: + checkSonsLen(n, 1) + result = semExpr(c, n.sons[0]) + of nkIfExpr: + result = semIfExpr(c, n) + of nkStmtListExpr: + result = semStmtListExpr(c, n) + of nkBlockExpr: + result = semBlockExpr(c, n) + of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: + checkSonsLen(n, 2) + of nkStringToCString, nkCStringToString, nkPassAsOpenArray, nkObjDownConv, + nkObjUpConv: + checkSonsLen(n, 1) + of nkChckRangeF, nkChckRange64, nkChckRange: + checkSonsLen(n, 3) + of nkCheckedFieldExpr: + checkMinSonsLen(n, 2) + of nkSymChoice: + liMessage(n.info, errExprXAmbiguous, renderTree(n, {renderNoComments})) + result = nil + else: + #InternalError(n.info, nodeKindToStr[n.kind]); + liMessage(n.info, errInvalidExpressionX, renderTree(n, {renderNoComments})) + result = nil + incl(result.flags, nfSem) |