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