Files
Nim/nim/semstmts.pas
2010-02-26 01:26:16 +01:00

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;