// // // 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; type TAssignableResult = ( arNone, // no l-value and no discriminant arLValue, // is an l-value arDiscriminant // is a discriminant ); function isAssignable(n: PNode): TAssignableResult; begin result := arNone; case n.kind of nkSym: begin if (n.sym.kind in [skVar, skTemp]) then result := arLValue end; nkDotExpr: begin checkMinSonsLen(n, 1); if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then result := arLValue else result := isAssignable(n.sons[0]); if (result = arLValue) and (sfDiscriminant in n.sons[1].sym.flags) then result := arDiscriminant end; nkBracketExpr: begin checkMinSonsLen(n, 1); if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then result := arLValue 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 := arLValue; 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 isAssignable(n) <> arLValue 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: 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); if n.sons[0].kind = nkDotExpr then 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, flags); exit end end else n.sons[0] := semExpr(c, n.sons[0]); 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: 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 not (sfProcVar in s.flags) and (s.typ.callConv = ccDefault) and (getModule(s).id <> c.module.id) then 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); 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);*) // 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); 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 isAssignable(n.sons[0]) <> arLValue 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;