mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-08 22:13:29 +00:00
1117 lines
33 KiB
ObjectPascal
Executable File
1117 lines
33 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 of statements
|
|
|
|
function semWhen(c: PContext; n: PNode): PNode;
|
|
var
|
|
i: int;
|
|
it, e: PNode;
|
|
begin
|
|
result := nil;
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
it := n.sons[i];
|
|
if it = nil then illFormedAst(n);
|
|
case it.kind of
|
|
nkElifBranch: begin
|
|
checkSonsLen(it, 2);
|
|
e := semConstExpr(c, it.sons[0]);
|
|
checkBool(e);
|
|
if (e.kind <> nkIntLit) then InternalError(n.info, 'semWhen');
|
|
if (e.intVal <> 0) and (result = nil) then
|
|
result := semStmt(c, it.sons[1]); // do not open a new scope!
|
|
end;
|
|
nkElse: begin
|
|
checkSonsLen(it, 1);
|
|
if result = nil then result := semStmt(c, it.sons[0])
|
|
// do not open a new scope!
|
|
end;
|
|
else illFormedAst(n)
|
|
end
|
|
end;
|
|
if result = nil then result := newNodeI(nkNilLit, n.info);
|
|
// The ``when`` statement implements the mechanism for platform dependant
|
|
// code. Thus we try to ensure here consistent ID allocation after the
|
|
// ``when`` statement.
|
|
IDsynchronizationPoint(200);
|
|
end;
|
|
|
|
function semIf(c: PContext; n: PNode): PNode;
|
|
var
|
|
i: int;
|
|
it: PNode;
|
|
begin
|
|
result := n;
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
it := n.sons[i];
|
|
if it = nil then illFormedAst(n);
|
|
case it.kind of
|
|
nkElifBranch: begin
|
|
checkSonsLen(it, 2);
|
|
openScope(c.tab);
|
|
it.sons[0] := semExprWithType(c, it.sons[0]);
|
|
checkBool(it.sons[0]);
|
|
it.sons[1] := semStmt(c, it.sons[1]);
|
|
closeScope(c.tab);
|
|
end;
|
|
nkElse: begin
|
|
if sonsLen(it) = 1 then it.sons[0] := semStmtScope(c, it.sons[0])
|
|
else illFormedAst(it)
|
|
end;
|
|
else illFormedAst(n)
|
|
end
|
|
end
|
|
end;
|
|
|
|
function semDiscard(c: PContext; n: PNode): PNode;
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, 1);
|
|
n.sons[0] := semExprWithType(c, n.sons[0]);
|
|
if n.sons[0].typ = nil then liMessage(n.info, errInvalidDiscard);
|
|
end;
|
|
|
|
function semBreakOrContinue(c: PContext; n: PNode): PNode;
|
|
var
|
|
s: PSym;
|
|
x: PNode;
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, 1);
|
|
if n.sons[0] <> nil then begin
|
|
case n.sons[0].kind of
|
|
nkIdent: s := lookUp(c, n.sons[0]);
|
|
nkSym: s := n.sons[0].sym;
|
|
else illFormedAst(n)
|
|
end;
|
|
if (s.kind = skLabel) and (s.owner.id = c.p.owner.id) then begin
|
|
x := newSymNode(s);
|
|
x.info := n.info;
|
|
include(s.flags, sfUsed);
|
|
n.sons[0] := x
|
|
end
|
|
else
|
|
liMessage(n.info, errInvalidControlFlowX, s.name.s)
|
|
end
|
|
else if (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0) then
|
|
liMessage(n.info, errInvalidControlFlowX,
|
|
renderTree(n, {@set}[renderNoComments]))
|
|
end;
|
|
|
|
function semBlock(c: PContext; n: PNode): PNode;
|
|
var
|
|
labl: PSym;
|
|
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
|
|
labl := newSymS(skLabel, n.sons[0], c);
|
|
addDecl(c, labl);
|
|
n.sons[0] := newSymNode(labl); // BUGFIX
|
|
end;
|
|
n.sons[1] := semStmt(c, n.sons[1]);
|
|
closeScope(c.tab);
|
|
Dec(c.p.nestedBlockCounter);
|
|
end;
|
|
|
|
function semAsm(con: PContext; n: PNode): PNode;
|
|
var
|
|
str, sub: string;
|
|
a, b, c: int;
|
|
e: PSym;
|
|
marker: char;
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, 2);
|
|
marker := pragmaAsm(con, n.sons[0]);
|
|
if marker = #0 then marker := '`'; // default marker
|
|
case n.sons[1].kind of
|
|
nkStrLit, nkRStrLit, nkTripleStrLit: begin
|
|
result := copyNode(n);
|
|
str := n.sons[1].strVal;
|
|
if str = '' then liMessage(n.info, errEmptyAsm);
|
|
// now parse the string literal and substitute symbols:
|
|
a := strStart;
|
|
repeat
|
|
b := strutils.find(str, marker, a);
|
|
if b < strStart then
|
|
sub := ncopy(str, a)
|
|
else
|
|
sub := ncopy(str, a, b-1);
|
|
if sub <> '' then
|
|
addSon(result, newStrNode(nkStrLit, sub));
|
|
|
|
if b < strStart then break;
|
|
c := strutils.find(str, marker, b+1);
|
|
if c < strStart then
|
|
sub := ncopy(str, b+1)
|
|
else
|
|
sub := ncopy(str, b+1, c-1);
|
|
if sub <> '' then begin
|
|
e := SymtabGet(con.tab, getIdent(sub));
|
|
if e <> nil then begin
|
|
if e.kind = skStub then loadStub(e);
|
|
addSon(result, newSymNode(e))
|
|
end
|
|
else
|
|
addSon(result, newStrNode(nkStrLit, sub));
|
|
end;
|
|
if c < strStart then break;
|
|
a := c+1;
|
|
until false;
|
|
end;
|
|
else illFormedAst(n)
|
|
end
|
|
end;
|
|
|
|
function semWhile(c: PContext; n: PNode): PNode;
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, 2);
|
|
openScope(c.tab);
|
|
n.sons[0] := semExprWithType(c, n.sons[0]);
|
|
CheckBool(n.sons[0]);
|
|
inc(c.p.nestedLoopCounter);
|
|
n.sons[1] := semStmt(c, n.sons[1]);
|
|
dec(c.p.nestedLoopCounter);
|
|
closeScope(c.tab);
|
|
end;
|
|
|
|
function semCase(c: PContext; n: PNode): PNode;
|
|
var
|
|
i, len: int;
|
|
covered: biggestint;
|
|
// for some types we count to check if all cases have been covered
|
|
chckCovered: boolean;
|
|
x: PNode;
|
|
begin
|
|
// check selector:
|
|
result := n;
|
|
checkMinSonsLen(n, 2);
|
|
openScope(c.tab);
|
|
n.sons[0] := semExprWithType(c, n.sons[0]);
|
|
chckCovered := false;
|
|
covered := 0;
|
|
case skipTypes(n.sons[0].Typ, abstractVarRange).Kind of
|
|
tyInt..tyInt64, tyChar, tyEnum: chckCovered := true;
|
|
tyFloat..tyFloat128, tyString: begin end
|
|
else liMessage(n.info, errSelectorMustBeOfCertainTypes);
|
|
end;
|
|
for i := 1 to sonsLen(n)-1 do begin
|
|
x := n.sons[i];
|
|
case x.kind of
|
|
nkOfBranch: begin
|
|
checkMinSonsLen(x, 2);
|
|
semCaseBranch(c, n, x, i, covered);
|
|
len := sonsLen(x);
|
|
x.sons[len-1] := semStmtScope(c, x.sons[len-1]);
|
|
end;
|
|
nkElifBranch: begin
|
|
chckCovered := false;
|
|
checkSonsLen(x, 2);
|
|
x.sons[0] := semExprWithType(c, x.sons[0]);
|
|
checkBool(x.sons[0]);
|
|
x.sons[1] := semStmtScope(c, x.sons[1])
|
|
end;
|
|
nkElse: begin
|
|
chckCovered := false;
|
|
checkSonsLen(x, 1);
|
|
x.sons[0] := semStmtScope(c, x.sons[0])
|
|
end;
|
|
else illFormedAst(x);
|
|
end;
|
|
end;
|
|
if chckCovered and (covered <> lengthOrd(n.sons[0].typ)) then
|
|
liMessage(n.info, errNotAllCasesCovered);
|
|
closeScope(c.tab);
|
|
end;
|
|
|
|
function semAsgn(c: PContext; n: PNode): PNode;
|
|
var
|
|
le: PType;
|
|
a: PNode;
|
|
id: PIdent;
|
|
begin
|
|
checkSonsLen(n, 2);
|
|
a := n.sons[0];
|
|
case a.kind of
|
|
nkDotExpr: begin
|
|
// r.f = x
|
|
// --> `f=` (r, x)
|
|
checkSonsLen(a, 2);
|
|
id := considerAcc(a.sons[1]);
|
|
result := newNodeI(nkCall, n.info);
|
|
addSon(result, newIdentNode(getIdent(id.s+'='), n.info));
|
|
addSon(result, semExpr(c, a.sons[0]));
|
|
addSon(result, semExpr(c, n.sons[1]));
|
|
result := semDirectCallAnalyseEffects(c, result, {@set}[]);
|
|
if result <> nil then begin
|
|
fixAbstractType(c, result);
|
|
analyseIfAddressTakenInCall(c, result);
|
|
exit;
|
|
end
|
|
end;
|
|
nkBracketExpr: begin
|
|
// a[i..j] = x
|
|
// --> `[..]=`(a, i, j, x)
|
|
result := newNodeI(nkCall, n.info);
|
|
checkSonsLen(a, 2);
|
|
if a.sons[1].kind = nkRange then begin
|
|
checkSonsLen(a.sons[1], 2);
|
|
addSon(result, newIdentNode(getIdent(whichSliceOpr(a.sons[1])+'='),
|
|
n.info));
|
|
addSon(result, semExpr(c, a.sons[0]));
|
|
addSonIfNotNil(result, semExpr(c, a.sons[1].sons[0]));
|
|
addSonIfNotNil(result, semExpr(c, a.sons[1].sons[1]));
|
|
addSon(result, semExpr(c, n.sons[1]));
|
|
result := semDirectCallAnalyseEffects(c, result, {@set}[]);
|
|
if result <> nil then begin
|
|
fixAbstractType(c, result);
|
|
analyseIfAddressTakenInCall(c, result);
|
|
exit;
|
|
end
|
|
end
|
|
else begin
|
|
addSon(result, newIdentNode(getIdent('[]='), n.info));
|
|
addSon(result, semExpr(c, a.sons[0]));
|
|
addSon(result, semExpr(c, a.sons[1]));
|
|
addSon(result, semExpr(c, n.sons[1]));
|
|
result := semDirectCallAnalyseEffects(c, result, {@set}[]);
|
|
if result <> nil then begin
|
|
fixAbstractType(c, result);
|
|
analyseIfAddressTakenInCall(c, result);
|
|
exit;
|
|
end
|
|
end;
|
|
end;
|
|
else begin end;
|
|
end;
|
|
n.sons[0] := semExprWithType(c, n.sons[0], {@set}[efLValue]);
|
|
n.sons[1] := semExprWithType(c, n.sons[1]);
|
|
le := n.sons[0].typ;
|
|
if (skipTypes(le, {@set}[tyGenericInst]).kind <> tyVar)
|
|
and (IsAssignable(n.sons[0]) = arNone) then begin
|
|
// Direct assignment to a discriminant is allowed!
|
|
liMessage(n.sons[0].info, errXCannotBeAssignedTo,
|
|
renderTree(n.sons[0], {@set}[renderNoComments]));
|
|
end
|
|
else begin
|
|
n.sons[1] := fitNode(c, le, n.sons[1]);
|
|
fixAbstractType(c, n);
|
|
end;
|
|
result := n;
|
|
end;
|
|
|
|
function SemReturn(c: PContext; n: PNode): PNode;
|
|
var
|
|
restype: PType;
|
|
a: PNode; // temporary assignment for code generator
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, 1);
|
|
if not (c.p.owner.kind in [skConverter, skMethod, skProc, skMacro]) then
|
|
liMessage(n.info, errXNotAllowedHere, '''return''');
|
|
if (n.sons[0] <> nil) then begin
|
|
n.sons[0] := SemExprWithType(c, n.sons[0]);
|
|
// check for type compatibility:
|
|
restype := c.p.owner.typ.sons[0];
|
|
if (restype <> nil) then begin
|
|
a := newNodeI(nkAsgn, n.sons[0].info);
|
|
|
|
n.sons[0] := fitNode(c, restype, n.sons[0]);
|
|
// optimize away ``return result``, because it would be transformed
|
|
// to ``result = result; return``:
|
|
if (n.sons[0].kind = nkSym) and (sfResult in n.sons[0].sym.flags) then
|
|
begin
|
|
n.sons[0] := nil;
|
|
end
|
|
else begin
|
|
if (c.p.resultSym = nil) then InternalError(n.info, 'semReturn');
|
|
addSon(a, semExprWithType(c, newSymNode(c.p.resultSym)));
|
|
addSon(a, n.sons[0]);
|
|
n.sons[0] := a;
|
|
end
|
|
end
|
|
else
|
|
liMessage(n.info, errCannotReturnExpr);
|
|
end;
|
|
end;
|
|
|
|
function SemYield(c: PContext; n: PNode): PNode;
|
|
var
|
|
restype: PType;
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, 1);
|
|
if (c.p.owner = nil) or (c.p.owner.kind <> skIterator) then
|
|
liMessage(n.info, errYieldNotAllowedHere);
|
|
if (n.sons[0] <> nil) then begin
|
|
n.sons[0] := SemExprWithType(c, n.sons[0]);
|
|
// check for type compatibility:
|
|
restype := c.p.owner.typ.sons[0];
|
|
if (restype <> nil) then begin
|
|
n.sons[0] := fitNode(c, restype, n.sons[0]);
|
|
if (n.sons[0].typ = nil) then InternalError(n.info, 'semYield');
|
|
end
|
|
else
|
|
liMessage(n.info, errCannotReturnExpr);
|
|
end
|
|
end;
|
|
|
|
function fitRemoveHiddenConv(c: PContext; typ: Ptype; n: PNode): PNode;
|
|
begin
|
|
result := fitNode(c, typ, n);
|
|
if (result.kind in [nkHiddenStdConv, nkHiddenSubConv]) then begin
|
|
changeType(result.sons[1], typ);
|
|
result := result.sons[1];
|
|
end
|
|
else if not sameType(result.typ, typ) then
|
|
changeType(result, typ)
|
|
end;
|
|
|
|
function semVar(c: PContext; n: PNode): PNode;
|
|
var
|
|
i, j, len: int;
|
|
a, b, def: PNode;
|
|
typ, tup: PType;
|
|
v: PSym;
|
|
begin
|
|
result := copyNode(n);
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
a := n.sons[i];
|
|
if a.kind = nkCommentStmt then continue;
|
|
if (a.kind <> nkIdentDefs) and (a.kind <> nkVarTuple) then IllFormedAst(a);
|
|
checkMinSonsLen(a, 3);
|
|
len := sonsLen(a);
|
|
if a.sons[len-2] <> nil then
|
|
typ := semTypeNode(c, a.sons[len-2], nil)
|
|
else
|
|
typ := nil;
|
|
if a.sons[len-1] <> nil then begin
|
|
def := semExprWithType(c, a.sons[len-1]);
|
|
// BUGFIX: ``fitNode`` is needed here!
|
|
// check type compability between def.typ and typ:
|
|
if (typ <> nil) then def := fitNode(c, typ, def)
|
|
else typ := def.typ;
|
|
end
|
|
else
|
|
def := nil;
|
|
if not typeAllowed(typ, skVar) then begin
|
|
//debug(typ);
|
|
liMessage(a.info, errXisNoType, typeToString(typ));
|
|
end;
|
|
tup := skipTypes(typ, {@set}[tyGenericInst]);
|
|
if a.kind = nkVarTuple then begin
|
|
if tup.kind <> tyTuple then liMessage(a.info, errXExpected, 'tuple');
|
|
if len-2 <> sonsLen(tup) then
|
|
liMessage(a.info, errWrongNumberOfVariables);
|
|
b := newNodeI(nkVarTuple, a.info);
|
|
newSons(b, len);
|
|
b.sons[len-2] := nil; // no type desc
|
|
b.sons[len-1] := def;
|
|
addSon(result, b);
|
|
end;
|
|
for j := 0 to len-3 do begin
|
|
if (c.p.owner.kind = skModule) then begin
|
|
v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[sfStar, sfMinus]);
|
|
include(v.flags, sfGlobal);
|
|
end
|
|
else
|
|
v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[]);
|
|
if v.flags * [sfStar, sfMinus] <> {@set}[] then
|
|
include(v.flags, sfInInterface);
|
|
addInterfaceDecl(c, v);
|
|
if a.kind <> nkVarTuple then begin
|
|
v.typ := typ;
|
|
b := newNodeI(nkIdentDefs, a.info);
|
|
addSon(b, newSymNode(v));
|
|
addSon(b, nil); // no type description
|
|
addSon(b, copyTree(def));
|
|
addSon(result, b);
|
|
end
|
|
else begin
|
|
v.typ := tup.sons[j];
|
|
b.sons[j] := newSymNode(v);
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
|
|
function semConst(c: PContext; n: PNode): PNode;
|
|
var
|
|
a, def, b: PNode;
|
|
i: int;
|
|
v: PSym;
|
|
typ: PType;
|
|
begin
|
|
result := copyNode(n);
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
a := n.sons[i];
|
|
if a.kind = nkCommentStmt then continue;
|
|
if (a.kind <> nkConstDef) then IllFormedAst(a);
|
|
checkSonsLen(a, 3);
|
|
if (c.p.owner.kind = skModule) then begin
|
|
v := semIdentWithPragma(c, skConst, a.sons[0], {@set}[sfStar, sfMinus]);
|
|
include(v.flags, sfGlobal);
|
|
end
|
|
else
|
|
v := semIdentWithPragma(c, skConst, a.sons[0], {@set}[]);
|
|
|
|
if a.sons[1] <> nil then typ := semTypeNode(c, a.sons[1], nil)
|
|
else typ := nil;
|
|
def := semAndEvalConstExpr(c, a.sons[2]);
|
|
// check type compability between def.typ and typ:
|
|
if (typ <> nil) then begin
|
|
def := fitRemoveHiddenConv(c, typ, def);
|
|
end
|
|
else typ := def.typ;
|
|
if not typeAllowed(typ, skConst) then
|
|
liMessage(a.info, errXisNoType, typeToString(typ));
|
|
|
|
v.typ := typ;
|
|
v.ast := def; // no need to copy
|
|
if v.flags * [sfStar, sfMinus] <> {@set}[] then
|
|
include(v.flags, sfInInterface);
|
|
addInterfaceDecl(c, v);
|
|
b := newNodeI(nkConstDef, a.info);
|
|
addSon(b, newSymNode(v));
|
|
addSon(b, nil); // no type description
|
|
addSon(b, copyTree(def));
|
|
addSon(result, b);
|
|
end;
|
|
end;
|
|
|
|
function semFor(c: PContext; n: PNode): PNode;
|
|
var
|
|
i, len: int;
|
|
v, countup: PSym;
|
|
iter: PType;
|
|
countupNode, call: PNode;
|
|
begin
|
|
result := n;
|
|
checkMinSonsLen(n, 3);
|
|
len := sonsLen(n);
|
|
openScope(c.tab);
|
|
if n.sons[len-2].kind = nkRange then begin
|
|
checkSonsLen(n.sons[len-2], 2);
|
|
// convert ``in 3..5`` to ``in countup(3, 5)``
|
|
countupNode := newNodeI(nkCall, n.sons[len-2].info);
|
|
countUp := StrTableGet(magicsys.systemModule.Tab, getIdent('countup'));
|
|
if (countUp = nil) then
|
|
liMessage(countupNode.info, errSystemNeeds, 'countup');
|
|
newSons(countupNode, 3);
|
|
countupnode.sons[0] := newSymNode(countup);
|
|
countupNode.sons[1] := n.sons[len-2].sons[0];
|
|
countupNode.sons[2] := n.sons[len-2].sons[1];
|
|
|
|
n.sons[len-2] := countupNode;
|
|
end;
|
|
n.sons[len-2] := semExprWithType(c, n.sons[len-2], {@set}[efWantIterator]);
|
|
call := n.sons[len-2];
|
|
if (call.kind <> nkCall) or (call.sons[0].kind <> nkSym)
|
|
or (call.sons[0].sym.kind <> skIterator) then
|
|
liMessage(n.sons[len-2].info, errIteratorExpected);
|
|
iter := skipTypes(n.sons[len-2].typ, {@set}[tyGenericInst]);
|
|
if iter.kind <> tyTuple then begin
|
|
if len <> 3 then liMessage(n.info, errWrongNumberOfVariables);
|
|
v := newSymS(skForVar, n.sons[0], c);
|
|
v.typ := iter;
|
|
n.sons[0] := newSymNode(v);
|
|
addDecl(c, v);
|
|
end
|
|
else begin
|
|
if len-2 <> sonsLen(iter) then liMessage(n.info, errWrongNumberOfVariables);
|
|
for i := 0 to len-3 do begin
|
|
v := newSymS(skForVar, n.sons[i], c);
|
|
v.typ := iter.sons[i];
|
|
n.sons[i] := newSymNode(v);
|
|
addDecl(c, v);
|
|
end
|
|
end;
|
|
// semantic checking for the sub statements:
|
|
Inc(c.p.nestedLoopCounter);
|
|
n.sons[len-1] := SemStmt(c, n.sons[len-1]);
|
|
closeScope(c.tab);
|
|
Dec(c.p.nestedLoopCounter);
|
|
end;
|
|
|
|
function semRaise(c: PContext; n: PNode): PNode;
|
|
var
|
|
typ: PType;
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, 1);
|
|
if n.sons[0] <> nil then begin
|
|
n.sons[0] := semExprWithType(c, n.sons[0]);
|
|
typ := n.sons[0].typ;
|
|
if (typ.kind <> tyRef) or (typ.sons[0].kind <> tyObject) then
|
|
liMessage(n.info, errExprCannotBeRaised)
|
|
end;
|
|
end;
|
|
|
|
function semTry(c: PContext; n: PNode): PNode;
|
|
var
|
|
i, j, len: int;
|
|
a: PNode;
|
|
typ: PType;
|
|
check: TIntSet;
|
|
begin
|
|
result := n;
|
|
checkMinSonsLen(n, 2);
|
|
n.sons[0] := semStmtScope(c, n.sons[0]);
|
|
IntSetInit(check);
|
|
for i := 1 to sonsLen(n)-1 do begin
|
|
a := n.sons[i];
|
|
checkMinSonsLen(a, 1);
|
|
len := sonsLen(a);
|
|
if a.kind = nkExceptBranch then begin
|
|
for j := 0 to len-2 do begin
|
|
typ := semTypeNode(c, a.sons[j], nil);
|
|
if typ.kind = tyRef then typ := typ.sons[0];
|
|
if (typ.kind <> tyObject) then
|
|
liMessage(a.sons[j].info, errExprCannotBeRaised);
|
|
a.sons[j] := newNodeI(nkType, a.sons[j].info);
|
|
a.sons[j].typ := typ;
|
|
if IntSetContainsOrIncl(check, typ.id) then
|
|
liMessage(a.sons[j].info, errExceptionAlreadyHandled);
|
|
end
|
|
end
|
|
else if a.kind <> nkFinally then
|
|
illFormedAst(n);
|
|
// last child of an nkExcept/nkFinally branch is a statement:
|
|
a.sons[len-1] := semStmtScope(c, a.sons[len-1]);
|
|
end;
|
|
end;
|
|
|
|
function semGenericParamList(c: PContext; n: PNode; father: PType = nil): PNode;
|
|
var
|
|
i, j, L: int;
|
|
s: PSym;
|
|
a, def: PNode;
|
|
typ: PType;
|
|
begin
|
|
result := copyNode(n);
|
|
if n.kind <> nkGenericParams then
|
|
InternalError(n.info, 'semGenericParamList');
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
a := n.sons[i];
|
|
if a.kind <> nkIdentDefs then illFormedAst(n);
|
|
L := sonsLen(a);
|
|
def := a.sons[L-1];
|
|
if a.sons[L-2] <> nil then
|
|
typ := semTypeNode(c, a.sons[L-2], nil)
|
|
else if def <> nil then
|
|
typ := newTypeS(tyExpr, c)
|
|
else
|
|
typ := nil;
|
|
for j := 0 to L-3 do begin
|
|
if (typ = nil) or (typ.kind = tyTypeDesc) then begin
|
|
s := newSymS(skType, a.sons[j], c);
|
|
s.typ := newTypeS(tyGenericParam, c)
|
|
end
|
|
else begin
|
|
s := newSymS(skGenericParam, a.sons[j], c);
|
|
s.typ := typ
|
|
end;
|
|
s.ast := def;
|
|
s.typ.sym := s;
|
|
if father <> nil then addSon(father, s.typ);
|
|
s.position := i;
|
|
addSon(result, newSymNode(s));
|
|
addDecl(c, s);
|
|
end
|
|
end
|
|
end;
|
|
|
|
procedure addGenericParamListToScope(c: PContext; n: PNode);
|
|
var
|
|
i: int;
|
|
a: PNode;
|
|
begin
|
|
if n.kind <> nkGenericParams then
|
|
InternalError(n.info, 'addGenericParamListToScope');
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
a := n.sons[i];
|
|
if a.kind <> nkSym then internalError(a.info, 'addGenericParamListToScope');
|
|
addDecl(c, a.sym)
|
|
end
|
|
end;
|
|
|
|
function SemTypeSection(c: PContext; n: PNode): PNode;
|
|
var
|
|
i: int;
|
|
s: PSym;
|
|
t, body: PType;
|
|
a: PNode;
|
|
begin
|
|
result := n;
|
|
// process the symbols on the left side for the whole type section, before
|
|
// we even look at the type definitions on the right
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
a := n.sons[i];
|
|
if a.kind = nkCommentStmt then continue;
|
|
if (a.kind <> nkTypeDef) then IllFormedAst(a);
|
|
checkSonsLen(a, 3);
|
|
if (c.p.owner.kind = skModule) then begin
|
|
s := semIdentWithPragma(c, skType, a.sons[0], {@set}[sfStar, sfMinus]);
|
|
include(s.flags, sfGlobal);
|
|
end
|
|
else
|
|
s := semIdentWithPragma(c, skType, a.sons[0], {@set}[]);
|
|
if s.flags * [sfStar, sfMinus] <> {@set}[] then
|
|
include(s.flags, sfInInterface);
|
|
s.typ := newTypeS(tyForward, c);
|
|
s.typ.sym := s;
|
|
// process pragmas:
|
|
if a.sons[0].kind = nkPragmaExpr then
|
|
pragma(c, s, a.sons[0].sons[1], typePragmas);
|
|
// add it here, so that recursive types are possible:
|
|
addInterfaceDecl(c, s);
|
|
a.sons[0] := newSymNode(s);
|
|
end;
|
|
|
|
// process the right side of the types:
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
a := n.sons[i];
|
|
if a.kind = nkCommentStmt then continue;
|
|
if (a.kind <> nkTypeDef) then IllFormedAst(a);
|
|
checkSonsLen(a, 3);
|
|
if (a.sons[0].kind <> nkSym) then IllFormedAst(a);
|
|
s := a.sons[0].sym;
|
|
if (s.magic = mNone) and (a.sons[2] = nil) then
|
|
liMessage(a.info, errImplOfXexpected, s.name.s);
|
|
if s.magic <> mNone then processMagicType(c, s);
|
|
if a.sons[1] <> nil then begin
|
|
// We have a generic type declaration here. In generic types,
|
|
// symbol lookup needs to be done here.
|
|
openScope(c.tab);
|
|
pushOwner(s);
|
|
s.typ.kind := tyGenericBody;
|
|
if s.typ.containerID <> 0 then
|
|
InternalError(a.info, 'semTypeSection: containerID');
|
|
s.typ.containerID := getID();
|
|
a.sons[1] := semGenericParamList(c, a.sons[1], s.typ);
|
|
addSon(s.typ, nil); // to be filled out later
|
|
s.ast := a;
|
|
body := semTypeNode(c, a.sons[2], nil);
|
|
if body <> nil then body.sym := s;
|
|
s.typ.sons[sonsLen(s.typ)-1] := body;
|
|
//debug(s.typ);
|
|
popOwner();
|
|
closeScope(c.tab);
|
|
end
|
|
else if a.sons[2] <> nil then begin
|
|
// process the type's body:
|
|
pushOwner(s);
|
|
t := semTypeNode(c, a.sons[2], s.typ);
|
|
if (t <> s.typ) and (s.typ <> nil) then
|
|
internalError(a.info, 'semTypeSection()');
|
|
s.typ := t;
|
|
s.ast := a;
|
|
popOwner();
|
|
end;
|
|
end;
|
|
// unfortunately we need another pass over the section for checking of
|
|
// illegal recursions and type aliases:
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
a := n.sons[i];
|
|
if a.kind = nkCommentStmt then continue;
|
|
if (a.sons[0].kind <> nkSym) then IllFormedAst(a);
|
|
s := a.sons[0].sym;
|
|
// compute the type's size and check for illegal recursions:
|
|
if a.sons[1] = nil then begin
|
|
if (a.sons[2] <> nil)
|
|
and (a.sons[2].kind in [nkSym, nkIdent, nkAccQuoted]) then begin
|
|
// type aliases are hard:
|
|
//MessageOut('for type ' + typeToString(s.typ));
|
|
t := semTypeNode(c, a.sons[2], nil);
|
|
if t.kind in [tyObject, tyEnum] then begin
|
|
assignType(s.typ, t);
|
|
s.typ.id := t.id; // same id
|
|
end
|
|
end;
|
|
checkConstructedType(s.info, s.typ);
|
|
end
|
|
end
|
|
end;
|
|
|
|
procedure semParamList(c: PContext; n, genericParams: PNode; s: PSym);
|
|
begin
|
|
s.typ := semProcTypeNode(c, n, genericParams, nil);
|
|
end;
|
|
|
|
procedure addParams(c: PContext; n: PNode);
|
|
var
|
|
i: int;
|
|
begin
|
|
for i := 1 to sonsLen(n)-1 do begin
|
|
if (n.sons[i].kind <> nkSym) then InternalError(n.info, 'addParams');
|
|
addDecl(c, n.sons[i].sym);
|
|
end
|
|
end;
|
|
|
|
procedure semBorrow(c: PContext; n: PNode; s: PSym);
|
|
var
|
|
b: PSym;
|
|
begin
|
|
// search for the correct alias:
|
|
b := SearchForBorrowProc(c, s, c.tab.tos-2);
|
|
if b = nil then liMessage(n.info, errNoSymbolToBorrowFromFound);
|
|
// store the alias:
|
|
n.sons[codePos] := newSymNode(b);
|
|
end;
|
|
|
|
procedure sideEffectsCheck(c: PContext; s: PSym);
|
|
begin
|
|
if [sfNoSideEffect, sfSideEffect] * s.flags =
|
|
[sfNoSideEffect, sfSideEffect] then
|
|
liMessage(s.info, errXhasSideEffects, s.name.s);
|
|
end;
|
|
|
|
procedure addResult(c: PContext; t: PType; const info: TLineInfo);
|
|
var
|
|
s: PSym;
|
|
begin
|
|
if t <> nil then begin
|
|
s := newSym(skVar, getIdent('result'), getCurrOwner());
|
|
s.info := info;
|
|
s.typ := t;
|
|
Include(s.flags, sfResult);
|
|
Include(s.flags, sfUsed);
|
|
addDecl(c, s);
|
|
c.p.resultSym := s;
|
|
end
|
|
end;
|
|
|
|
procedure addResultNode(c: PContext; n: PNode);
|
|
begin
|
|
if c.p.resultSym <> nil then addSon(n, newSymNode(c.p.resultSym));
|
|
end;
|
|
|
|
function semLambda(c: PContext; n: PNode): PNode;
|
|
var
|
|
s: PSym;
|
|
oldP: PProcCon;
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, codePos+1);
|
|
s := newSym(skProc, getIdent(':anonymous'), getCurrOwner());
|
|
s.info := n.info;
|
|
|
|
oldP := c.p; // restore later
|
|
s.ast := n;
|
|
n.sons[namePos] := newSymNode(s);
|
|
|
|
pushOwner(s);
|
|
openScope(c.tab);
|
|
if (n.sons[genericParamsPos] <> nil) then illFormedAst(n);
|
|
// process parameters:
|
|
if n.sons[paramsPos] <> nil then begin
|
|
semParamList(c, n.sons[ParamsPos], nil, s);
|
|
addParams(c, s.typ.n);
|
|
end
|
|
else begin
|
|
s.typ := newTypeS(tyProc, c);
|
|
addSon(s.typ, nil);
|
|
end;
|
|
|
|
// we are in a nested proc:
|
|
s.typ.callConv := ccClosure;
|
|
if n.sons[pragmasPos] <> nil then
|
|
pragma(c, s, n.sons[pragmasPos], lambdaPragmas);
|
|
|
|
s.options := gOptions;
|
|
if n.sons[codePos] <> nil then begin
|
|
if sfImportc in s.flags then
|
|
liMessage(n.sons[codePos].info, errImplOfXNotAllowed, s.name.s);
|
|
c.p := newProcCon(s);
|
|
addResult(c, s.typ.sons[0], n.info);
|
|
n.sons[codePos] := semStmtScope(c, n.sons[codePos]);
|
|
addResultNode(c, n);
|
|
end
|
|
else
|
|
liMessage(n.info, errImplOfXexpected, s.name.s);
|
|
closeScope(c.tab); // close scope for parameters
|
|
popOwner();
|
|
c.p := oldP; // restore
|
|
result.typ := s.typ;
|
|
end;
|
|
|
|
function semProcAux(c: PContext; n: PNode; kind: TSymKind;
|
|
const validPragmas: TSpecialWords): PNode;
|
|
var
|
|
s, proto: PSym;
|
|
oldP: PProcCon;
|
|
gp: PNode;
|
|
begin
|
|
result := n;
|
|
checkSonsLen(n, codePos+1);
|
|
if c.p.owner.kind = skModule then begin
|
|
s := semIdentVis(c, kind, n.sons[0], {@set}[sfStar]);
|
|
include(s.flags, sfGlobal);
|
|
end
|
|
else
|
|
s := semIdentVis(c, kind, n.sons[0], {@set}[]);
|
|
n.sons[namePos] := newSymNode(s);
|
|
oldP := c.p; // restore later
|
|
if sfStar in s.flags then include(s.flags, sfInInterface);
|
|
s.ast := n;
|
|
|
|
pushOwner(s);
|
|
openScope(c.tab);
|
|
if n.sons[genericParamsPos] <> nil then begin
|
|
n.sons[genericParamsPos] := semGenericParamList(c, n.sons[genericParamsPos]);
|
|
gp := n.sons[genericParamsPos]
|
|
end
|
|
else
|
|
gp := newNodeI(nkGenericParams, n.info);
|
|
// process parameters:
|
|
if n.sons[paramsPos] <> nil then begin
|
|
semParamList(c, n.sons[ParamsPos], gp, s);
|
|
if sonsLen(gp) > 0 then n.sons[genericParamsPos] := gp;
|
|
addParams(c, s.typ.n);
|
|
end
|
|
else begin
|
|
s.typ := newTypeS(tyProc, c);
|
|
addSon(s.typ, nil);
|
|
end;
|
|
|
|
proto := SearchForProc(c, s, c.tab.tos-2); // -2 because we have a scope open
|
|
// for parameters
|
|
if proto = nil then begin
|
|
if oldP.owner.kind <> skModule then // we are in a nested proc
|
|
s.typ.callConv := ccClosure
|
|
else
|
|
s.typ.callConv := lastOptionEntry(c).defaultCC;
|
|
// add it here, so that recursive procs are possible:
|
|
// -2 because we have a scope open for parameters
|
|
if kind in OverloadableSyms then
|
|
addInterfaceOverloadableSymAt(c, s, c.tab.tos-2)
|
|
else
|
|
addDeclAt(c, s, c.tab.tos-2);
|
|
if n.sons[pragmasPos] <> nil then
|
|
pragma(c, s, n.sons[pragmasPos], validPragmas)
|
|
end
|
|
else begin
|
|
if n.sons[pragmasPos] <> nil then
|
|
liMessage(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc);
|
|
if not (sfForward in proto.flags) then
|
|
liMessage(n.info, errAttemptToRedefineX, proto.name.s);
|
|
exclude(proto.flags, sfForward);
|
|
closeScope(c.tab); // close scope with wrong parameter symbols
|
|
openScope(c.tab); // open scope for old (correct) parameter symbols
|
|
if proto.ast.sons[genericParamsPos] <> nil then
|
|
addGenericParamListToScope(c, proto.ast.sons[genericParamsPos]);
|
|
addParams(c, proto.typ.n);
|
|
proto.info := s.info; // more accurate line information
|
|
s.typ := proto.typ;
|
|
s := proto;
|
|
n.sons[genericParamsPos] := proto.ast.sons[genericParamsPos];
|
|
n.sons[paramsPos] := proto.ast.sons[paramsPos];
|
|
if (n.sons[namePos].kind <> nkSym) then InternalError(n.info, 'semProcAux');
|
|
n.sons[namePos].sym := proto;
|
|
proto.ast := n; // needed for code generation
|
|
popOwner();
|
|
pushOwner(s);
|
|
end;
|
|
|
|
s.options := gOptions;
|
|
if n.sons[codePos] <> nil then begin
|
|
if [sfImportc, sfBorrow] * s.flags <> [] then
|
|
liMessage(n.sons[codePos].info, errImplOfXNotAllowed, s.name.s);
|
|
if (n.sons[genericParamsPos] = nil) then begin
|
|
c.p := newProcCon(s);
|
|
if (s.typ.sons[0] <> nil) and (kind <> skIterator) then
|
|
addResult(c, s.typ.sons[0], n.info);
|
|
n.sons[codePos] := semStmtScope(c, n.sons[codePos]);
|
|
if (s.typ.sons[0] <> nil) and (kind <> skIterator) then
|
|
addResultNode(c, n);
|
|
end
|
|
else begin
|
|
if (s.typ.sons[0] <> nil) and (kind <> skIterator) then
|
|
addDecl(c, newSym(skUnknown, getIdent('result'), nil));
|
|
n.sons[codePos] := semGenericStmtScope(c, n.sons[codePos]);
|
|
end
|
|
end
|
|
else begin
|
|
if proto <> nil then
|
|
liMessage(n.info, errImplOfXexpected, proto.name.s);
|
|
if [sfImportc, sfBorrow] * s.flags = [] then Include(s.flags, sfForward)
|
|
else if sfBorrow in s.flags then
|
|
semBorrow(c, n, s);
|
|
end;
|
|
sideEffectsCheck(c, s);
|
|
closeScope(c.tab); // close scope for parameters
|
|
popOwner();
|
|
c.p := oldP; // restore
|
|
end;
|
|
|
|
function semIterator(c: PContext; n: PNode): PNode;
|
|
var
|
|
t: PType;
|
|
s: PSym;
|
|
begin
|
|
result := semProcAux(c, n, skIterator, iteratorPragmas);
|
|
s := result.sons[namePos].sym;
|
|
t := s.typ;
|
|
if t.sons[0] = nil then liMessage(n.info, errXNeedsReturnType, 'iterator');
|
|
if n.sons[codePos] = nil then liMessage(n.info, errImplOfXexpected, s.name.s);
|
|
end;
|
|
|
|
function semProc(c: PContext; n: PNode): PNode;
|
|
begin
|
|
result := semProcAux(c, n, skProc, procPragmas);
|
|
end;
|
|
|
|
function semMethod(c: PContext; n: PNode): PNode;
|
|
begin
|
|
if not isTopLevel(c) then
|
|
liMessage(n.info, errXOnlyAtModuleScope, 'method');
|
|
result := semProcAux(c, n, skMethod, methodPragmas);
|
|
end;
|
|
|
|
function semConverterDef(c: PContext; n: PNode): PNode;
|
|
var
|
|
t: PType;
|
|
s: PSym;
|
|
begin
|
|
if not isTopLevel(c) then
|
|
liMessage(n.info, errXOnlyAtModuleScope, 'converter');
|
|
checkSonsLen(n, codePos+1);
|
|
if n.sons[genericParamsPos] <> nil then
|
|
liMessage(n.info, errNoGenericParamsAllowedForX, 'converter');
|
|
result := semProcAux(c, n, skConverter, converterPragmas);
|
|
s := result.sons[namePos].sym;
|
|
t := s.typ;
|
|
if t.sons[0] = nil then liMessage(n.info, errXNeedsReturnType, 'converter');
|
|
if sonsLen(t) <> 2 then liMessage(n.info, errXRequiresOneArgument, 'converter');
|
|
addConverter(c, s);
|
|
end;
|
|
|
|
function semMacroDef(c: PContext; n: PNode): PNode;
|
|
var
|
|
t: PType;
|
|
s: PSym;
|
|
begin
|
|
checkSonsLen(n, codePos+1);
|
|
if n.sons[genericParamsPos] <> nil then
|
|
liMessage(n.info, errNoGenericParamsAllowedForX, 'macro');
|
|
result := semProcAux(c, n, skMacro, macroPragmas);
|
|
s := result.sons[namePos].sym;
|
|
t := s.typ;
|
|
if t.sons[0] = nil then liMessage(n.info, errXNeedsReturnType, 'macro');
|
|
if sonsLen(t) <> 2 then liMessage(n.info, errXRequiresOneArgument, 'macro');
|
|
if n.sons[codePos] = nil then liMessage(n.info, errImplOfXexpected, s.name.s);
|
|
end;
|
|
|
|
function evalInclude(c: PContext; n: PNode): PNode;
|
|
var
|
|
i, fileIndex: int;
|
|
f: string;
|
|
begin
|
|
result := newNodeI(nkStmtList, n.info);
|
|
addSon(result, n); // the rodwriter needs include information!
|
|
for i := 0 to sonsLen(n)-1 do begin
|
|
f := getModuleFile(n.sons[i]);
|
|
fileIndex := includeFilename(f);
|
|
if IntSetContainsOrIncl(c.includedFiles, fileIndex) then
|
|
liMessage(n.info, errRecursiveDependencyX, f);
|
|
addSon(result, semStmt(c, gIncludeFile(f)));
|
|
IntSetExcl(c.includedFiles, fileIndex);
|
|
end;
|
|
end;
|
|
|
|
function semCommand(c: PContext; n: PNode): PNode;
|
|
begin
|
|
result := semExpr(c, n);
|
|
if result.typ <> nil then liMessage(n.info, errDiscardValue);
|
|
end;
|
|
|
|
function SemStmt(c: PContext; n: PNode): PNode;
|
|
const
|
|
// must be last statements in a block:
|
|
LastBlockStmts = {@set}[nkRaiseStmt, nkReturnStmt, nkBreakStmt,
|
|
nkContinueStmt];
|
|
var
|
|
len, i, j: int;
|
|
begin
|
|
result := n;
|
|
if n = nil then exit;
|
|
if nfSem in n.flags then exit;
|
|
case n.kind of
|
|
nkAsgn: result := semAsgn(c, n);
|
|
nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkMacroStmt, nkCallStrLit:
|
|
result := semCommand(c, n);
|
|
nkEmpty, nkCommentStmt, nkNilLit: begin end;
|
|
nkBlockStmt: result := semBlock(c, n);
|
|
nkStmtList: begin
|
|
len := sonsLen(n);
|
|
for i := 0 to len-1 do begin
|
|
n.sons[i] := semStmt(c, n.sons[i]);
|
|
if (n.sons[i].kind in LastBlockStmts) then begin
|
|
for j := i+1 to len-1 do
|
|
case n.sons[j].kind of
|
|
nkPragma, nkCommentStmt, nkNilLit, nkEmpty: begin end;
|
|
else liMessage(n.sons[j].info, errStmtInvalidAfterReturn);
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
nkRaiseStmt: result := semRaise(c, n);
|
|
nkVarSection: result := semVar(c, n);
|
|
nkConstSection: result := semConst(c, n);
|
|
nkTypeSection: result := SemTypeSection(c, n);
|
|
nkIfStmt: result := SemIf(c, n);
|
|
nkWhenStmt: result := semWhen(c, n);
|
|
nkDiscardStmt: result := semDiscard(c, n);
|
|
nkWhileStmt: result := semWhile(c, n);
|
|
nkTryStmt: result := semTry(c, n);
|
|
nkBreakStmt, nkContinueStmt: result := semBreakOrContinue(c, n);
|
|
nkForStmt: result := semFor(c, n);
|
|
nkCaseStmt: result := semCase(c, n);
|
|
nkReturnStmt: result := semReturn(c, n);
|
|
nkAsmStmt: result := semAsm(c, n);
|
|
nkYieldStmt: result := semYield(c, n);
|
|
nkPragma: pragma(c, c.p.owner, n, stmtPragmas);
|
|
nkIteratorDef: result := semIterator(c, n);
|
|
nkProcDef: result := semProc(c, n);
|
|
nkMethodDef: result := semMethod(c, n);
|
|
nkConverterDef: result := semConverterDef(c, n);
|
|
nkMacroDef: result := semMacroDef(c, n);
|
|
nkTemplateDef: result := semTemplateDef(c, n);
|
|
nkImportStmt: begin
|
|
if not isTopLevel(c) then
|
|
liMessage(n.info, errXOnlyAtModuleScope, 'import');
|
|
result := evalImport(c, n);
|
|
end;
|
|
nkFromStmt: begin
|
|
if not isTopLevel(c) then
|
|
liMessage(n.info, errXOnlyAtModuleScope, 'from');
|
|
result := evalFrom(c, n);
|
|
end;
|
|
nkIncludeStmt: begin
|
|
if not isTopLevel(c) then
|
|
liMessage(n.info, errXOnlyAtModuleScope, 'include');
|
|
result := evalInclude(c, n);
|
|
end;
|
|
else liMessage(n.info, errStmtExpected);
|
|
end;
|
|
if result = nil then InternalError(n.info, 'SemStmt: result = nil');
|
|
include(result.flags, nfSem);
|
|
end;
|
|
|
|
function semStmtScope(c: PContext; n: PNode): PNode;
|
|
begin
|
|
openScope(c.tab);
|
|
result := semStmt(c, n);
|
|
closeScope(c.tab);
|
|
end;
|