summary refs log blame commit diff stats
path: root/nim/semexprs.pas
blob: 59d7f969a6076a2a49fed6bd2474e2b2db15fdf8 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12


                                         
                                          







                                                         

                                                                  

                                               


                                                              
     
                                 

                                                        

                                                            




                                                                   




                                                              
            
     
                                         


                                              















































                                                                              




                                                                         



                                                                             
                         


                                     
       
                           

                                                       



                                                        





                                                                



                                               


                                                                                
                                               


                                                        











                                                                               
                                                                    






















                                                        
                                                                    



                             













                                                                      
                                





                                                








                                          
                         
                             

                                                              


                             
                                         













                                                          

                                              








                                                            
              

                

                                                                             


                                     























                                                                           





                   




                                                      
                                        


                                           
                                                                        








                                                  
                                                                       

    


                                                            
                                                             








                                                 





                                                               

                                                                               




                                                                             
                                                
                                      

                                    
           




                                                                      
                                 
































                                                                            


                                                                             

















                                                                     


                                                 










                                                             
         







                                                                
         









                                                            


     

                                                             
                                                                   

                                                             

                                                       











                                                                      
 






                                                     
                        

                                 

                                            




                                                                      
                                     


            
                                            










                                                                        

                                                 
          

                                                                    







                                           
                                                       


                                                                  
                                         



                                                   
                                                







                                                             
                                         







                                                                     

                                             



                                                      

                                                                         
















                                                                

                                             











                                                       
                                                                             
                                 
                         














                                                                     

                         
                                               
       
              
                                                              






                                                  
                     


















                                                         
                                     






                                                          
                                              








                                                          
                                                          




                                               




                                                                                
                                                                          




                           
                                                          









                                                                  
                                             













                                                                            

                        










                                         
                                                              
                                                                            































































































                                                                             



            
               
     

                                                                          

                                                                  














                                                     
                            




                                                         
                                                                         



                                                  





                                                             
                             

                                     




                                                                 
                                          
                                                                    
                       
                              





                                       


            







                                        
                            

          



                                      



                                                                           
                                          
                                                                             
                                            






                                                









                                                                         


                        


                  
                         


                                                                  


                                                              
                                        
                                   
                                                                        







                                                                     
                                  

                  
                                        













                                                                     












                                                                                 




                                   






                                                 
                     




                                       

                                                     
                              
                                                     



                                                      

                                                     
                                                                

                                                  
                           










                                                    



                                            




                                                                    


                                                                   



                                                                            
                                                   


                                                       




                                                
                                                           



                                            
                                               











                                                      
                                                                 





                                      
                                                  

                                                   

                                                                    
                                
                                          
                                              
                                                                      













                                                            
                                                            






               
                                    
                              
                                                      


                                          
                                                            
                              



                                            

                                                         
                                                               

                                                
                       






                                       
                                                               





                                                          
                     
                                     
                                               









                                                       
                        




                                       
                                                       







                                                    







                                                              


                              
                                                                     




                                                          
                                    
      
                                                    
                                          

    


















































                                                                        
                                                                             




                       
                                


                  

                                       



                               
                                                              
                                              

                                                            




                                                                  
                                                                               

                                      











                                                                               
                                 
                                        

                                           
                                           




                                                          
                            

                                                
                              












                                            
                                    

                                                             









                                                               


                                   
                        
                            







                                                                                
                                              






                                                                        
                

                              
                                                                  

                                                            

          


                                                               




                                                      







                                                              


                  


                                                                                 

                                             


                                                
        
                                    
                      
                         






                                                      











                                                                           
              
                                                     



                                                         

                               
    
//
//
//           The Ethexor Morpork 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 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 = 0 then
  //  liMessage(info, hintConvToBaseNotNeeded)
  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 := skipVarGeneric(castDest);
  s := skipVarGeneric(src);
  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 (skipVarGenericRange(castDest).Kind in IntegralTypes)
  and (skipVarGenericRange(src).Kind in IntegralTypes) then begin
    // accept conversion between intregral types
  end
  else begin
    case cmpTypes(d, s) of
      isNone, isGeneric:
        // we use d, s here to speed up that operation a bit
        liMessage(info, errGenerated,
          format(MsgKindToString(errIllegalConvFromXtoY),
            [typeToString(src), typeToString(castDest)]));
      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
      (skipGeneric(dst).kind in [tyInt..tyFloat128]) or
      (skipGeneric(src).kind in [tyInt..tyFloat128])
end;

function semConv(c: PContext; n: PNode; s: PSym): PNode;
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]));
  checkConvertible(result.info, result.typ, result.sons[1].typ);
end;

function semCast(c: PContext; n: PNode): PNode;
begin
  if optSafeCode in gGlobalOptions then liMessage(n.info, errCastNotInSafeMode);
  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 := skipVarGenericRange(n.sons[1].typ);
    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 := skipVar(result.sons[0].typ);
    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 skipVarGeneric(it.typ).kind = tyOpenArray then begin
          s := skipVarGeneric(it.sons[1].typ);
          if (s.kind = tyArrayConstr) and (s.sons[1].kind = tyEmpty) then begin
            s := copyType(s, getCurrOwner(), false);
            skipVarGeneric(s).sons[1] := elemType(skipVarGeneric(it.typ));
            it.sons[1].typ := s;
          end
        end
        else if skipVarGeneric(it.sons[1].typ).kind in [tyNil, tyArrayConstr,
                      tyTuple, tySet] then begin
          s := skipVarGeneric(it.typ);
          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 skipPtrsGeneric(n.sons[1].typ).kind in [tyTuple, tyObject] then
        result := n.sons[1]
      else
        result := n
    end;
    nkObjUpConv, nkObjDownConv: result := n.sons[0];
    else result := n
  end
end;

function isAssignable(n: PNode): bool;
begin
  result := false;
  case n.kind of
    nkSym: result := n.sym.kind in [skVar, skTemp];
    nkDotExpr, nkQualified, nkBracketExpr: begin
      checkMinSonsLen(n, 1);
      if skipGeneric(n.sons[0].typ).kind in [tyVar, tyPtr, tyRef] then
        result := true
      else
        result := isAssignable(n.sons[0]);
    end;
    nkHiddenStdConv, nkHiddenSubConv, nkConv: begin
      // Object and tuple conversions are still addressable, so we skip them
      //if skipPtrsGeneric(n.sons[1].typ).kind in [tyOpenArray,
      //                                           tyTuple, tyObject] then
      if skipPtrsGeneric(n.typ).kind in [tyOpenArray, tyTuple, tyObject] then
        result := isAssignable(n.sons[1])
    end;
    nkHiddenDeref, nkDerefExpr: result := true;
    nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
      result := isAssignable(n.sons[0]);
    else begin end
  end
end;

function newHiddenAddrTaken(c: PContext; n: PNode): PNode;
begin
  if n.kind = nkHiddenDeref then begin
    checkSonsLen(n, 1);
    result := n.sons[0]
  end
  else begin
    result := newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ));
    addSon(result, n);
    if not isAssignable(n) then begin
      liMessage(n.info, errVarForOutParamNeeded);
    end
  end
end;

function analyseIfAddressTaken(c: PContext; n: PNode): PNode;
begin
  result := n;
  case n.kind of
    nkSym: begin
      if skipGeneric(n.sym.typ).kind <> tyVar then begin
        include(n.sym.flags, sfAddrTaken);
        result := newHiddenAddrTaken(c, n);
      end
    end;
    nkDotExpr, nkQualified: begin
      checkSonsLen(n, 2);
      if n.sons[1].kind <> nkSym then
        internalError(n.info, 'analyseIfAddressTaken');
      if skipGeneric(n.sons[1].sym.typ).kind <> tyVar then begin
        include(n.sons[1].sym.flags, sfAddrTaken);
        result := newHiddenAddrTaken(c, n);
      end
    end;
    nkBracketExpr: begin
      checkMinSonsLen(n, 1);
      if skipGeneric(n.sons[0].typ).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, mAppendSeqSeq,
                         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 (skipGeneric(t.sons[i]).kind = tyVar) then
      n.sons[i] := analyseIfAddressTaken(c, n.sons[i]);
end;

function semIndirectOp(c: PContext; n: PNode): PNode;
var
  m: TCandidate;
  msg: string;
  i: int;
begin
  result := nil;
  checkMinSonsLen(n, 1);
  case n.sons[0].kind of
    nkDotExpr, nkQualified: begin
      checkSonsLen(n.sons[0], 2);
      n.sons[0] := semDotExpr(c, n.sons[0]);
      if n.sons[0].kind = nkDotCall then begin // it is a static call!
        result := n.sons[0];
        result.kind := nkCall;
        for i := 1 to sonsLen(n)-1 do
          addSon(result, n.sons[i]);
        result := semExpr(c, result);
        exit
      end
    end;
    else n.sons[0] := semExpr(c, n.sons[0]);
  end;
  if n.sons[0].typ = nil then
    liMessage(n.sons[0].info, errExprXHasNoType,
              renderTree(n.sons[0], {@set}[renderNoComments]));
  semOpAux(c, n);
  if (n.sons[0].typ <> nil) and (n.sons[0].typ.kind = tyProc) then begin
    initCandidate(m, n.sons[0].typ);
    matches(c, n, m);
    if m.state <> csMatch then begin
      msg := msgKindToString(errTypeMismatch);
      for i := 1 to sonsLen(n)-1 do begin
        add(msg, typeToString(n.sons[i].typ));
        if i <> sonsLen(n)-1 then add(msg, ', ');
      end;
      add(msg, ')' +{&} nl +{&} msgKindToString(errButExpected) +{&}
             nl +{&} typeToString(n.sons[0].typ));
      liMessage(n.Info, errGenerated, msg);
      result := nil
    end
    else
      result := m.call;
  end
  else begin
    result := overloadedCallOpr(c, n);
    if result = nil then result := semDirectCall(c, n);
    if result = nil then liMessage(n.info, errExprCannotBeCalled);
  end;
  fixAbstractType(c, result);
  analyseIfAddressTakenInCall(c, result);
end;

function semDirectOp(c: PContext; n: PNode): PNode;
begin
  // this seems to be a hotspot in the compiler!
  semOpAux(c, n);
  result := semDirectCall(c, n);
  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 semIncSucc(c: PContext; n: PNode; const opr: string): PNode;
// handles Inc, Dec, Succ and Pred
var
  a: PNode;
  typ: PType;
begin
  checkMinSonsLen(n, 1);
  n.sons[1] := semExprWithType(c, n.sons[1]);
  typ := skipVar(n.sons[1].Typ);
  if not isOrdinalType(typ) or enumHasWholes(typ) then
    liMessage(n.sons[1].Info, errOrdinalTypeExpected);
  if sonsLen(n) = 3 then begin
    n.sons[2] := semExprWithType(c, n.sons[2]);
    a := IndexTypesMatch(c, getSysType(tyInt), n.sons[2].typ, n.sons[2]);
    if a = nil then
      typeMismatch(n.sons[2], getSysType(tyInt), n.sons[2].typ);
    n.sons[2] := a;
  end
  else if sonsLen(n) = 2 then begin // default value of 1
    a := newIntNode(nkIntLit, 1);
    a.info := n.info;
    a.typ := getSysType(tyInt);
    addSon(n, a)
  end
  else
    liMessage(n.info, errInvalidArgForX, opr);
  result := n;
end;

function semOrd(c: PContext; n: PNode): PNode;
begin
  checkSonsLen(n, 2);
  n.sons[1] := semExprWithType(c, n.sons[1]);
  if not isOrdinalType(skipVar(n.sons[1].Typ)) then
    liMessage(n.Info, errOrdinalTypeExpected);
  n.typ := getSysType(tyInt);
  result := n
end;

function LookUpForDefined(c: PContext; n: PNode): PSym;
var
  m: PSym;
  ident: PIdent;
begin
  case n.kind of
    nkIdent: result := SymtabGet(c.Tab, n.ident); // no need for stub loading
    nkDotExpr, nkQualified: begin
      checkSonsLen(n, 2);
      result := nil;
      m := LookupForDefined(c, n.sons[0]);
      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]);
    end
    else begin
      liMessage(n.info, errIdentifierExpected, renderTree(n));
      result := nil;
    end
  end
end;

function semDefined(c: PContext; n: PNode): 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]) <> nil then
    result.intVal := 1
  else if (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): 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));
    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));
    mSucc:    begin
      result := semIncSucc(c, setMs(n, s), 'succ');
      result.typ := n.sons[1].typ;
    end;
    mPred:    begin
      result := semIncSucc(c, setMs(n, s), 'pred');
      result.typ := n.sons[1].typ;
    end;
    mInc:     result := semIncSucc(c, setMs(n, s), 'inc');
    ast.mDec: result := semIncSucc(c, setMs(n, s), 'dec');
    mOrd:     result := semOrd(c, setMs(n, s));
    else      result := semDirectOp(c, n);
  end;
end;

procedure checkDeprecated(n: PNode; s: PSym);
begin
  if sfDeprecated in s.flags then liMessage(n.info, warnDeprecated, s.name.s);  
end;

function semSym(c: PContext; n: PNode; s: PSym; flags: TExprFlags): PNode;
begin
  result := newSymNode(s);
  result.info := n.info;
  result.typ := s.typ;
  include(s.flags, sfUsed);
  if (s.kind = skType) and not (efAllowType in flags) then
    liMessage(n.info, errATypeHasNoValue);
  case s.kind of
    skProc, skIterator, skConverter:
      if (s.magic <> mNone) then
        liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
    skConst: begin
      (*
        Consider::
          const x = []
          proc p(a: openarray[int], i: int)
          proc q(a: openarray[char], c: char)
          p(x, 0)
          q(x, '\0')

        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 ``[]``.
      *)
      if s.typ.kind in ConstAbstractTypes then begin
        result := copyTree(s.ast);
        result.info := n.info;
        result.typ := s.typ;
      end
    end
    else begin end
  end;
  checkDeprecated(n, s);
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 := n.typ;
  result := n;
  if t.kind = tyVar then begin
    result := newNodeIT(nkHiddenDeref, n.info, t.sons[0]);
    addSon(result, n);
    t := t.sons[0];
  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;
      checkDeprecated(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 := skipPtrsGeneric(ty);
  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 := skipGeneric(ty.sons[0]);
    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;
        checkDeprecated(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;
      checkDeprecated(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);
  // XXX ``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, 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 := skipPtrsGeneric(n.sons[0].typ);
  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 skipRange(n.sons[1].typ).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 := skipVar(n.sons[i].sons[0].typ);
        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 := skipVar(n.sons[i].typ)
      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 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 isCallExpr(n: PNode): bool;
begin
  result := n.kind in [nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand];
end;

function semMacroStmt(c: PContext; n: PNode): 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
    checkDeprecated(n, s);
    case s.kind of
      skMacro: begin
        include(s.flags, sfUsed);
        result := semMacroExpr(c, n, s);
      end;
      skTemplate: begin
        include(s.flags, sfUsed);
        // 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]);
        pushInfoContext(n.info);
        result := evalTemplate(c, result, s);
        popInfoContext();
      end;
      else
        liMessage(n.info, errXisNoMacroOrTemplate, s.name.s);
    end
  end
  else
    liMessage(n.info, errInvalidExpressionX,
              renderTree(a, {@set}[renderNoComments]));
end;

function semExpr(c: PContext; n: PNode; flags: TExprFlags = {@set}[]): PNode;
var
  s: PSym;
begin
  result := n;
  if n = nil then exit;
  if nfSem in n.flags then exit;
  case n.kind of
    // atoms:
    nkIdent: begin
      s := lookUp(c, n);
      result := semSym(c, n, s, flags);
    end;
    nkSym: begin
      s := n.sym;
      include(s.flags, sfUsed);
      if (s.kind = skType) and not (efAllowType in flags) then
        liMessage(n.info, errATypeHasNoValue);
      if (s.magic <> mNone) and
          (s.kind in [skProc, skIterator, skConverter]) then
        liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
    end;
    nkEmpty, nkNone: begin end;
    nkNilLit: result.typ := getSysType(tyNil);
    nkType: begin
      if not (efAllowType in flags) then liMessage(n.info, errATypeHasNoValue);
      n.typ := semTypeNode(c, n, nil);
    end;
    nkIntLit: if result.typ = nil then result.typ := getSysType(tyInt);
    nkInt8Lit: if result.typ = nil then result.typ := getSysType(tyInt8);
    nkInt16Lit: if result.typ = nil then result.typ := getSysType(tyInt16);
    nkInt32Lit: if result.typ = nil then result.typ := getSysType(tyInt32);
    nkInt64Lit: if result.typ = nil then result.typ := getSysType(tyInt64);
    nkFloatLit: if result.typ = nil then result.typ := getSysType(tyFloat);
    nkFloat32Lit: if result.typ = nil then result.typ := getSysType(tyFloat32);
    nkFloat64Lit: if result.typ = nil then result.typ := getSysType(tyFloat64);
    nkStrLit..nkTripleStrLit:
      if result.typ = nil then result.typ := getSysType(tyString);
    nkCharLit:
      if result.typ = nil then result.typ := getSysType(tyChar);
    nkQualified, nkDotExpr: begin
      result := semDotExpr(c, n, flags);
      if result.kind = nkDotCall then begin
        result.kind := nkCall;
        result := semExpr(c, result, flags)
      end;
    end;
    // complex expressions
    nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand: begin
      // check if it is an expression macro:
      checkMinSonsLen(n, 1);
      s := qualifiedLookup(c, n.sons[0], false);
      if (s <> nil) then begin
        checkDeprecated(n, s);
        case s.kind of
          skMacro: begin
            include(s.flags, sfUsed);
            result := semMacroExpr(c, n, s);
          end;
          skTemplate: begin
            include(s.flags, sfUsed);
            pushInfoContext(n.info);
            result := evalTemplate(c, n, s);
            popInfoContext();
          end;
          skType: begin
            include(s.flags, sfUsed);
            if n.kind <> nkCall then
              liMessage(n.info, errXisNotCallable, s.name.s);
            result := semConv(c, n, s);
          end;
          skProc, skConverter, skIterator: begin
            if s.magic = mNone then result := semDirectOp(c, n)
            else result := semMagic(c, n, s);
          end;
          else result := semIndirectOp(c, n)
        end
      end
      else result := semIndirectOp(c, n);
    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, 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); // handled in semstmts
    nkExprColonExpr: begin
      internalError(n.info, 'semExpr() to implement');
      // XXX: to implement for array constructors!
    end;
    nkDerefExpr: begin
      checkSonsLen(n, 1);
      n.sons[0] := semExprWithType(c, n.sons[0]);
      result := n;
      case skipVarGeneric(n.sons[0].typ).kind of
        tyRef, tyPtr: n.typ := n.sons[0].typ.sons[0];
        else liMessage(n.sons[0].info, errCircumNeedsPointer);
      end;
      result := n;
    end;
    nkAddr: begin
      result := n;
      checkSonsLen(n, 1);
      n.sons[0] := semExprWithType(c, n.sons[0]);
      if not isAssignable(n.sons[0]) then liMessage(n.info, errExprHasNoAddress);
      n.typ := makePtrType(c, n.sons[0].typ);
    end;
    nkHiddenAddr, nkHiddenDeref: begin
      checkSonsLen(n, 1);
      n.sons[0] := semExpr(c, n.sons[0], flags);
    end;
    nkCast: result := semCast(c, n);
    nkAccQuoted: begin
      checkSonsLen(n, 1);
      result := semExpr(c, n.sons[0]);
    end;
    nkHeaderQuoted: begin
      // look up the proc:
      internalError(n.info, 'semExpr() to implement');
      // XXX: to implement
    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);
    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;