mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 17:34:43 +00:00
1427 lines
44 KiB
ObjectPascal
Executable File
1427 lines
44 KiB
ObjectPascal
Executable File
//
|
|
//
|
|
// 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;
|