fpc/compiler/pdecvar.pas

2144 lines
87 KiB
ObjectPascal

{
Copyright (c) 1998-2002 by Florian Klaempfl
Parses variable declarations. Used for var statement and record
definitions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit pdecvar;
{$i fpcdefs.inc}
interface
uses
cclasses,
symtable,symsym,symdef,symtype;
type
tvar_dec_option=(vd_record,vd_object,vd_threadvar,vd_class,vd_final,vd_canreorder,vd_check_generic);
tvar_dec_options=set of tvar_dec_option;
function read_property_dec(is_classproperty:boolean;astruct:tabstractrecorddef):tpropertysym;
procedure read_var_decls(options:Tvar_dec_options;out had_generic:boolean);
procedure read_record_fields(options:Tvar_dec_options; reorderlist: TFPObjectList; variantdesc: ppvariantrecdesc;out had_generic:boolean; out attr_element_count : integer);
procedure read_public_and_external(vs: tabstractvarsym);
procedure try_consume_sectiondirective(var asection: ansistring);
function check_allowed_for_var_or_const(def:tdef;allowdynarray:boolean):boolean;
implementation
uses
SysUtils,
{ common }
cutils,
{ global }
globtype,globals,tokens,verbose,constexp,
systems,
{ symtable }
symconst,symbase,defutil,defcmp,symutil,symcreat,
{$if defined(i386) or defined(i8086) or defined(wasm)}
symcpu,
{$endif}
fmodule,htypechk,procdefutil,
{ pass 1 }
node,pass_1,aasmbase,aasmdata,
ncon,nset,ncnv,nld,nutils,
{ codegen }
ngenutil,
{ parser }
scanner,
pbase,pexpr,ptype,ptconst,pdecsub,pparautl;
function read_property_dec(is_classproperty:boolean;astruct:tabstractrecorddef):tpropertysym;
{ convert a node tree to symlist and return the last
symbol }
function parse_symlist(pl:tpropaccesslist;out def:tdef):boolean;
var
idx : longint;
sym : tsym;
srsymtable : TSymtable;
st : TSymtable;
p : tnode;
begin
result:=true;
def:=nil;
if token=_ID then
begin
if assigned(astruct) then
sym:=search_struct_member(astruct,pattern)
else
searchsym(pattern,sym,srsymtable);
if assigned(sym) then
begin
if assigned(astruct) and
not is_visible_for_object(sym,astruct) then
Message(parser_e_cant_access_private_member);
case sym.typ of
fieldvarsym :
begin
addsymref(sym);
pl.addsym(sl_load,sym);
def:=tfieldvarsym(sym).vardef;
end;
procsym :
begin
addsymref(sym);
pl.addsym(sl_call,sym);
end;
else
begin
Message1(parser_e_illegal_field_or_method,orgpattern);
def:=generrordef;
result:=false;
end;
end;
end
else
begin
Message1(parser_e_illegal_field_or_method,orgpattern);
def:=generrordef;
result:=false;
end;
consume(_ID);
repeat
case token of
_ID,
_SEMICOLON :
begin
break;
end;
_POINT :
begin
if not is_object(def) and not is_record(def) then
message(sym_e_type_must_be_rec_or_object);
consume(_POINT);
if assigned(def) then
begin
st:=def.GetSymtable(gs_record);
if assigned(st) then
begin
sym:=tsym(st.Find(pattern));
if not(assigned(sym)) and is_object(def) then
sym:=search_struct_member(tobjectdef(def),pattern);
if assigned(sym) then
begin
pl.addsym(sl_subscript,sym);
case sym.typ of
fieldvarsym :
def:=tfieldvarsym(sym).vardef;
else
begin
Message1(sym_e_illegal_field,orgpattern);
result:=false;
end;
end;
end
else
begin
Message1(sym_e_illegal_field,orgpattern);
result:=false;
end;
end
else
begin
Message(parser_e_invalid_qualifier);
result:=false;
end;
end
else
begin
Message(parser_e_invalid_qualifier);
result:=false;
end;
consume(_ID);
end;
_LECKKLAMMER :
begin
consume(_LECKKLAMMER);
repeat
if assigned(def) and (def.typ=arraydef) then
begin
idx:=0;
p:=comp_expr([ef_accept_equal]);
if (not codegenerror) then
begin
if (p.nodetype=ordconstn) then
begin
{ type/range checking }
inserttypeconv(p,tarraydef(def).rangedef);
if (Tordconstnode(p).value<int64(low(longint))) or
(Tordconstnode(p).value>int64(high(longint))) then
message(parser_e_array_range_out_of_bounds)
else
idx:=Tordconstnode(p).value.svalue
end
else
Message(type_e_ordinal_expr_expected)
end;
pl.addconst(sl_vec,idx,p.resultdef);
p.free;
def:=tarraydef(def).elementdef;
end
else
begin
Message(parser_e_invalid_qualifier);
result:=false;
end;
until not try_to_consume(_COMMA);
consume(_RECKKLAMMER);
end;
else
begin
Message(parser_e_ill_property_access_sym);
result:=false;
break;
end;
end;
until false;
end
else
begin
Message(parser_e_ill_property_access_sym);
result:=false;
end;
end;
function has_implicit_default(p : tpropertysym) : boolean;
begin
has_implicit_default:=
(is_string(p.propdef) or
is_real(p.propdef) or
is_pointer(p.propdef));
end;
function allow_default_property(p : tpropertysym) : boolean;
begin
allow_default_property:=
(is_ordinal(p.propdef) or
{$ifndef cpu64bitaddr}
is_64bitint(p.propdef) or
{$endif cpu64bitaddr}
is_class(p.propdef) or
is_single(p.propdef) or
(p.propdef.typ in [classrefdef,pointerdef]) or
is_smallset(p.propdef)
) and not
(
(p.propdef.typ=arraydef) and
(ppo_indexed in p.propoptions)
) and not
(ppo_hasparameters in p.propoptions);
end;
procedure create_accessor_procsym(p: tpropertysym; pd: tprocdef; const prefix: string;
accesstype: tpropaccesslisttypes);
var
sym: tprocsym;
begin
if not assigned(astruct) then
handle_calling_convention(pd,hcc_default_actions_intf)
else
handle_calling_convention(pd,hcc_default_actions_intf_struct);
sym:=cprocsym.create(prefix+lower(p.realname));
symtablestack.top.insertsym(sym);
pd.procsym:=sym;
include(pd.procoptions,po_dispid);
include(pd.procoptions,po_global);
pd.visibility:=vis_private;
proc_add_definition(pd);
p.propaccesslist[accesstype].addsym(sl_call,sym);
p.propaccesslist[accesstype].procdef:=pd;
end;
procedure parse_dispinterface(p : tpropertysym; readpd,writepd: tprocdef;
var paranr: word);
var
hasread, haswrite: boolean;
pt: tnode;
hdispid: longint;
hparavs: tparavarsym;
begin
p.propaccesslist[palt_read].clear;
p.propaccesslist[palt_write].clear;
hasread:=true;
haswrite:=true;
hdispid:=0;
if try_to_consume(_READONLY) then
haswrite:=false
else if try_to_consume(_WRITEONLY) then
hasread:=false;
if try_to_consume(_DISPID) then
begin
pt:=comp_expr([ef_accept_equal]);
if is_constintnode(pt) then
if (Tordconstnode(pt).value<int64(low(longint))) or (Tordconstnode(pt).value>int64(high(longint))) then
message3(type_e_range_check_error_bounds,tostr(Tordconstnode(pt).value),tostr(low(longint)),tostr(high(longint)))
else
hdispid:=Tordconstnode(pt).value.svalue
else
Message(parser_e_dispid_must_be_ord_const);
pt.free;
end
else
hdispid:=tobjectdef(astruct).get_next_dispid;
{ COM property is simply a pair of methods, tagged with 'propertyget'
and 'propertyset' flags (or a single method if access is restricted).
Creating these implicit accessor methods also allows the rest of compiler
to handle dispinterface properties the same way as regular ones. }
if hasread then
begin
readpd.returndef:=p.propdef;
readpd.dispid:=hdispid;
readpd.proctypeoption:=potype_propgetter;
create_accessor_procsym(p,readpd,'get$',palt_read);
end;
if haswrite then
begin
{ add an extra parameter, a placeholder of the value to set }
inc(paranr);
hparavs:=cparavarsym.create('$value',10*paranr,vs_value,p.propdef,[]);
writepd.parast.insertsym(hparavs);
writepd.proctypeoption:=potype_propsetter;
writepd.dispid:=hdispid;
create_accessor_procsym(p,writepd,'put$',palt_write);
end;
end;
var
sym : tsym;
srsymtable: tsymtable;
p : tpropertysym;
overridden : tsym;
varspez : tvarspez;
hdef : tdef;
arraytype : tdef;
def : tdef;
pt : tnode;
sc : TFPObjectList;
paranr : word;
i : longint;
ImplIntf : TImplementedInterface;
found,
gotreadorwrite: boolean;
hreadparavs,
hparavs : tparavarsym;
storedprocdef: tprocvardef;
readprocdef,
writeprocdef : tprocdef;
begin
result:=nil;
{ Generate temp procdefs to search for matching read/write
procedures. the readprocdef will store all definitions }
paranr:=0;
readprocdef:=cprocdef.create(normal_function_level,false);
writeprocdef:=cprocdef.create(normal_function_level,false);
readprocdef.struct:=astruct;
writeprocdef.struct:=astruct;
if assigned(astruct) and is_classproperty then
begin
readprocdef.procoptions:=[po_staticmethod,po_classmethod];
writeprocdef.procoptions:=[po_staticmethod,po_classmethod];
end;
if token<>_ID then
begin
consume(_ID);
consume(_SEMICOLON);
exit;
end;
{ Generate propertysym and insert in symtablestack }
p:=cpropertysym.create(orgpattern);
p.visibility:=symtablestack.top.currentvisibility;
p.default:=longint($80000000);
if is_classproperty then
include(p.symoptions, sp_static);
symtablestack.top.insertsym(p);
consume(_ID);
{ property parameters ? }
if try_to_consume(_LECKKLAMMER) then
begin
if (p.visibility=vis_published) and
not (m_delphi in current_settings.modeswitches) then
Message(parser_e_cant_publish_that_property);
{ create a list of the parameters }
p.parast:=tparasymtable.create(nil,0);
symtablestack.push(p.parast);
sc:=TFPObjectList.create(false);
repeat
if try_to_consume(_VAR) then
varspez:=vs_var
else if try_to_consume(_CONST) then
varspez:=vs_const
else if try_to_consume(_CONSTREF) then
varspez:=vs_constref
else if (m_out in current_settings.modeswitches) and try_to_consume(_OUT) then
varspez:=vs_out
else
varspez:=vs_value;
sc.clear;
repeat
inc(paranr);
hreadparavs:=cparavarsym.create(orgpattern,10*paranr,varspez,generrordef,[]);
p.parast.insertsym(hreadparavs);
sc.add(hreadparavs);
consume(_ID);
until not try_to_consume(_COMMA);
if try_to_consume(_COLON) then
begin
if try_to_consume(_ARRAY) then
begin
consume(_OF);
{ define range and type of range }
hdef:=carraydef.create_openarray;
hdef.owner:=astruct.symtable;
{ define field type }
single_type(arraytype,[]);
tarraydef(hdef).elementdef:=arraytype;
end
else
single_type(hdef,[]);
end
else
hdef:=cformaltype;
for i:=0 to sc.count-1 do
tparavarsym(sc[i]).vardef:=hdef;
until not try_to_consume(_SEMICOLON);
sc.free;
symtablestack.pop(p.parast);
consume(_RECKKLAMMER);
{ the parser need to know if a property has parameters, the
index parameter doesn't count (PFV) }
if paranr>0 then
begin
p.add_accessor_parameters(readprocdef,writeprocdef);
include(p.propoptions,ppo_hasparameters);
end;
end;
{ overridden property ? }
{ force property interface
there is a property parameter
a global property }
if (token=_COLON) or (paranr>0) or (astruct=nil) then
begin
consume(_COLON);
single_type(p.propdef,[stoAllowSpecialization]);
if is_dispinterface(astruct) and not is_automatable(p.propdef) then
Message1(type_e_not_automatable,p.propdef.typename);
if (idtoken=_INDEX) then
begin
consume(_INDEX);
pt:=comp_expr([ef_accept_equal]);
{ Only allow enum and integer indexes. Convert all integer
values to objpas.integer (s32int on 32- and 64-bit targets,
s16int on 16- and 8-bit) to be compatible with delphi,
because the procedure matching requires equal parameters }
if is_constnode(pt) and
is_ordinal(pt.resultdef)
and (not is_64bitint(pt.resultdef))
{$if defined(cpu8bitalu) or defined(cpu16bitalu)}
and (not is_32bitint(pt.resultdef))
{$endif}
then
begin
if is_integer(pt.resultdef) then
{$if defined(cpu8bitalu) or defined(cpu16bitalu)}
inserttypeconv_internal(pt,s16inttype);
{$else}
inserttypeconv_internal(pt,s32inttype);
{$endif}
p.index:=tordconstnode(pt).value.svalue;
end
else
begin
Message(parser_e_invalid_property_index_value);
p.index:=0;
end;
p.indexdef:=pt.resultdef;
include(p.propoptions,ppo_indexed);
{ concat a longint to the para templates }
p.add_index_parameter(paranr,readprocdef,writeprocdef);
pt.free;
end;
end
else
begin
{ do an property override }
if (astruct.typ=objectdef) and assigned(tobjectdef(astruct).childof) then
overridden:=search_struct_member(tobjectdef(astruct).childof,p.name)
else
overridden:=nil;
if assigned(overridden) and
(overridden.typ=propertysym) and
not(is_dispinterface(astruct)) then
begin
tpropertysym(overridden).makeduplicate(p,readprocdef,writeprocdef,paranr);
p.register_override(tpropertysym(overridden));
end
else
begin
p.propdef:=generrordef;
message(parser_e_no_property_found_to_override);
end;
end;
if ((p.visibility=vis_published) or is_dispinterface(astruct)) then
begin
{ ignore is_publishable for interfaces (related to $M+ directive).
$M has effect on visibility of default section for classes.
Interface has always only public section (fix for problem in tb0631.pp) }
if (sp_static in p.symoptions) or ((p.propdef.is_publishable=pp_error) and not is_interface(astruct)) then
begin
Message(parser_e_cant_publish_that_property);
p.visibility:=vis_public;
end
else
if (p.propdef.is_publishable=pp_ignore) and not is_interface(astruct) then
begin
Message(parser_w_ignoring_published_property);
p.visibility:=vis_public;
end;
end;
if not(is_dispinterface(astruct)) then
begin
gotreadorwrite:=false;
{ parse accessors }
if try_to_consume(_READ) then
begin
gotreadorwrite:=true;
p.propaccesslist[palt_read].clear;
if parse_symlist(p.propaccesslist[palt_read],def) then
begin
sym:=p.propaccesslist[palt_read].firstsym^.sym;
{ getter is a function returning the type of the property }
if sym.typ=procsym then
begin
readprocdef.returndef:=p.propdef;
{ Insert hidden parameters }
if assigned(astruct) then
handle_calling_convention(readprocdef,hcc_default_actions_intf_struct)
else
handle_calling_convention(readprocdef,hcc_default_actions_intf);
end;
p.add_getter_or_setter_for_sym(palt_read,sym,def,readprocdef);
end;
end
else
p.inherit_accessor(palt_read);
if try_to_consume(_WRITE) then
begin
gotreadorwrite:=true;
p.propaccesslist[palt_write].clear;
if parse_symlist(p.propaccesslist[palt_write],def) then
begin
sym:=p.propaccesslist[palt_write].firstsym^.sym;
if sym.typ=procsym then
begin
{ settter is a procedure with an extra value parameter
of the of the property }
writeprocdef.returndef:=voidtype;
inc(paranr);
hparavs:=cparavarsym.create('$value',10*paranr,vs_value,p.propdef,[]);
writeprocdef.parast.insertsym(hparavs);
{ Insert hidden parameters }
if not assigned(astruct) then
handle_calling_convention(writeprocdef,hcc_default_actions_intf)
else
handle_calling_convention(writeprocdef,hcc_default_actions_intf_struct);
end;
p.add_getter_or_setter_for_sym(palt_write,sym,def,writeprocdef);
end;
end
else
p.inherit_accessor(palt_write);
{ a new property (needs to declare a getter or setter, except in
an interface }
if not(ppo_overrides in p.propoptions) and
not is_interface(astruct) and
not gotreadorwrite then
Consume(_READ);
end
else
parse_dispinterface(p,readprocdef,writeprocdef,paranr);
{ stored is not allowed for dispinterfaces, records or class properties }
if assigned(astruct) and not(is_dispinterface(astruct) or is_record(astruct)) and not is_classproperty then
begin
{ ppo_stored is default on for not overridden properties }
if not assigned(p.overriddenpropsym) then
include(p.propoptions,ppo_stored);
if try_to_consume(_STORED) then
begin
include(p.propoptions,ppo_stored);
p.propaccesslist[palt_stored].clear;
if token=_ID then
begin
{ in the case that idtoken=_DEFAULT }
{ we have to do nothing except }
{ setting ppo_stored, it's the same }
{ as stored true }
if idtoken<>_DEFAULT then
begin
{ parse_symlist cannot deal with constsyms, and
we also don't want to put constsyms in symlists
since they have to be evaluated immediately rather
than each time the property is accessed
The proper fix would be to always create a parse tree
and then convert that one, if appropriate, to a symlist.
Currently, we e.g. don't support any constant expressions
yet either here, while Delphi does.
}
{ make sure we don't let constants mask class fields/
methods
}
sym:=nil;
if (not assigned(astruct) or
(search_struct_member(astruct,pattern)=nil)) and
searchsym(pattern,sym,srsymtable) and
(sym.typ = constsym) then
begin
addsymref(sym);
if not is_boolean(tconstsym(sym).constdef) then
Message(parser_e_stored_property_must_be_boolean)
else if (tconstsym(sym).value.valueord=0) then
{ same as for _FALSE }
exclude(p.propoptions,ppo_stored)
else
begin
{ same as for _TRUE }
{ do nothing - ppo_stored is already set to p.propoptions in "include(p.propoptions,ppo_stored);" above }
{ especially do not reset the default value - the stored specifier is independent on the default value! }
end;
consume(_ID);
end
else if parse_symlist(p.propaccesslist[palt_stored],def) then
begin
sym:=p.propaccesslist[palt_stored].firstsym^.sym;
case sym.typ of
procsym :
begin
{ Create a temporary procvardef to handle parameters }
storedprocdef:=cprocvardef.create(normal_function_level,true);
include(storedprocdef.procoptions,po_methodpointer);
{ Return type must be boolean }
storedprocdef.returndef:=pasbool1type;
{ Add index parameter if needed }
if ppo_indexed in p.propoptions then
begin
hparavs:=cparavarsym.create('$index',10,vs_value,p.indexdef,[]);
storedprocdef.parast.insertsym(hparavs);
end;
{ Insert hidden parameters }
if not assigned(astruct) then
handle_calling_convention(storedprocdef,hcc_default_actions_intf)
else
handle_calling_convention(storedprocdef,hcc_default_actions_intf_struct);
p.propaccesslist[palt_stored].procdef:=Tprocsym(sym).Find_procdef_bypara(storedprocdef.paras,storedprocdef.returndef,[cpo_allowdefaults,cpo_ignorehidden]);
if not assigned(p.propaccesslist[palt_stored].procdef) then
message(parser_e_ill_property_storage_sym);
{ Not needed anymore }
storedprocdef.owner.deletedef(storedprocdef);
end;
fieldvarsym :
begin
if not assigned(def) then
internalerror(200310073);
if (ppo_hasparameters in p.propoptions) or
not(is_boolean(def)) then
Message(parser_e_stored_property_must_be_boolean);
end;
else
Message(parser_e_ill_property_access_sym);
end;
end;
end;
end;
end;
end;
if has_implicit_default(p) and not assigned(p.overriddenpropsym) then
begin
p.default:=0;
end;
if not is_record(astruct) and try_to_consume(_DEFAULT) then
begin
if not allow_default_property(p) then
begin
Message(parser_e_property_cant_have_a_default_value);
{ Error recovery }
pt:=comp_expr([ef_accept_equal]);
pt.free;
end
else
begin
{ Get the result of the default, the firstpass is
needed to support values like -1 }
pt:=comp_expr([ef_accept_equal]);
if (p.propdef.typ=setdef) and
(pt.nodetype=arrayconstructorn) then
begin
arrayconstructor_to_set(pt);
do_typecheckpass(pt);
end;
inserttypeconv(pt,p.propdef);
if not(is_constnode(pt)) then
Message(parser_e_property_default_value_must_const);
{ Set default value }
case pt.nodetype of
setconstn :
p.default:=plongint(tsetconstnode(pt).value_set)^;
ordconstn :
if (Tordconstnode(pt).value<int64(low(longint))) or
(Tordconstnode(pt).value>int64(high(cardinal))) then
message3(type_e_range_check_error_bounds,tostr(Tordconstnode(pt).value),tostr(low(longint)),tostr(high(cardinal)))
else
p.default:=longint(tordconstnode(pt).value.svalue);
niln :
p.default:=0;
realconstn:
p.default:=longint(single(trealconstnode(pt).value_real));
else if not codegenerror then
internalerror(2019050525);
end;
pt.free;
end;
end
else if not is_record(astruct) and try_to_consume(_NODEFAULT) then
begin
p.default:=longint($80000000);
end;
(*
else {if allow_default_property(p) then
begin
p.default:=longint($80000000);
end;
*)
{ Parse possible "implements" keyword }
if not is_record(astruct) and try_to_consume(_IMPLEMENTS) then
repeat
single_type(def,[]);
if not(is_interface(def)) then
message(parser_e_class_implements_must_be_interface);
if is_interface(p.propdef) then
begin
{ an interface type may delegate itself or one of its ancestors }
if not def_is_related(p.propdef,def) then
begin
message2(parser_e_implements_must_have_correct_type,def.typename,p.propdef.typename);
exit;
end;
end
else if is_class(p.propdef) then
begin
ImplIntf:=find_implemented_interface(tobjectdef(p.propdef),tobjectdef(def));
if assigned(ImplIntf) then
begin
if compare_defs(ImplIntf.IntfDef,def,nothingn)<te_equal then
begin
message2(parser_e_implements_must_have_correct_type,ImplIntf.IntfDef.typename,def.typename);
exit;
end;
end
else
begin
message2(parser_e_class_doesnt_implement_interface,p.propdef.typename,def.typename);
exit;
end;
end
else
begin
message(parser_e_implements_must_be_class_or_interface);
exit;
end;
if not assigned(p.propaccesslist[palt_read].firstsym) then
begin
message(parser_e_implements_must_read_specifier);
exit;
end;
if assigned(p.propaccesslist[palt_read].procdef) and
(tprocdef(p.propaccesslist[palt_read].procdef).proccalloption<>pocall_default) then
message(parser_e_implements_getter_not_default_cc);
if assigned(p.propaccesslist[palt_write].firstsym) then
begin
message(parser_e_implements_must_not_have_write_specifier);
exit;
end;
if assigned(p.propaccesslist[palt_stored].firstsym) then
begin
message(parser_e_implements_must_not_have_stored_specifier);
exit;
end;
found:=false;
ImplIntf:=nil;
for i:=0 to tobjectdef(astruct).ImplementedInterfaces.Count-1 do
begin
ImplIntf:=TImplementedInterface(tobjectdef(astruct).ImplementedInterfaces[i]);
if compare_defs(def,ImplIntf.IntfDef,nothingn)>=te_equal then
begin
found:=true;
break;
end;
end;
if found then
begin
{ An interface may not be delegated by more than one property,
it also may not have method mappings. }
if Assigned(ImplIntf.ImplementsGetter) then
message1(parser_e_duplicate_implements_clause,ImplIntf.IntfDef.typename);
if Assigned(ImplIntf.NameMappings) then
message2(parser_e_mapping_no_implements,ImplIntf.IntfDef.typename,astruct.objrealname^);
ImplIntf.ImplementsGetter:=p;
ImplIntf.VtblImplIntf:=ImplIntf;
case p.propaccesslist[palt_read].firstsym^.sym.typ of
procsym :
begin
if (po_virtualmethod in tprocdef(p.propaccesslist[palt_read].procdef).procoptions) and
not is_objectpascal_helper(tprocdef(p.propaccesslist[palt_read].procdef).struct) then
ImplIntf.IType:=etVirtualMethodResult
else
ImplIntf.IType:=etStaticMethodResult;
end;
fieldvarsym :
begin
ImplIntf.IType:=etFieldValue;
{ this must be done in a more robust way. Can't read the
fieldvarsym's fieldoffset yet, because it may not yet
be set }
ImplIntf.ImplementsField:=p.propaccesslist[palt_read].firstsym^.sym;
end
else
internalerror(200802161);
end;
if not is_interface(p.propdef) then
case ImplIntf.IType of
etVirtualMethodResult: ImplIntf.IType := etVirtualMethodClass;
etStaticMethodResult: ImplIntf.IType := etStaticMethodClass;
etFieldValue: ImplIntf.IType := etFieldValueClass;
else
internalerror(200912101);
end;
end
else
message1(parser_e_implements_uses_non_implemented_interface,def.typename);
until not try_to_consume(_COMMA);
{ register propgetter and propsetter procdefs }
if assigned(current_module) and current_module.in_interface then
begin
if readprocdef.proctypeoption=potype_propgetter then
readprocdef.register_def
else
readprocdef.free;
if writeprocdef.proctypeoption=potype_propsetter then
writeprocdef.register_def
else
writeprocdef.free;
end
else
begin
if readprocdef.proctypeoption=potype_propgetter then
readprocdef.maybe_put_in_symtable_stack
else
readprocdef.free;
if writeprocdef.proctypeoption=potype_propsetter then
writeprocdef.maybe_put_in_symtable_stack
else
writeprocdef.free;
end;
result:=p;
end;
function maybe_parse_proc_directives(def:tdef):boolean;
begin
result:=false;
{ Process procvar directives before = and ; }
if (
(def.typ=procvardef) or
is_funcref(def)
) and
(def.typesym=nil) and
check_proc_directive(true) then
begin
parse_proctype_directives(def);
result:=true;
end;
end;
const
variantrecordlevel : longint = 0;
procedure read_public_and_external_sc(sc:TFPObjectList);
var
vs: tabstractvarsym;
begin
{ only allowed for one var }
vs:=tabstractvarsym(sc[0]);
if sc.count>1 then
Message1(parser_e_directive_only_one_var,arraytokeninfo[idtoken].str);
read_public_and_external(vs);
end;
procedure read_public_and_external(vs: tabstractvarsym);
var
is_dll,
is_far,
is_cdecl,
is_external_var,
is_weak_external,
is_public_var : boolean;
dll_name,section_name,
C_name,mangledname : string;
begin
{ only allowed for one var }
{ only allow external and public on global symbols }
if vs.typ<>staticvarsym then
begin
Message(parser_e_no_local_var_external);
exit;
end;
{ defaults }
is_dll:=false;
is_far:=false;
is_cdecl:=false;
is_external_var:=false;
is_public_var:=false;
section_name := '';
dll_name := '';
C_name:=vs.realname;
{ macpas specific handling due to some switches}
if (m_mac in current_settings.modeswitches) then
begin
if (cs_external_var in current_settings.localswitches) then
begin {The effect of this is the same as if cvar; external; has been given as directives.}
is_cdecl:=true;
is_external_var:=true;
end
else if (cs_externally_visible in current_settings.localswitches) then
begin {The effect of this is the same as if cvar has been given as directives and it's made public.}
is_cdecl:=true;
is_public_var:=true;
end;
end;
{ cdecl }
if try_to_consume(_CVAR) then
begin
consume(_SEMICOLON);
is_cdecl:=true;
end;
{ external }
is_weak_external:=try_to_consume(_WEAKEXTERNAL);
if is_weak_external or
try_to_consume(_EXTERNAL) then
begin
is_external_var:=true;
{ near/far? }
if target_info.system in systems_allow_external_far_var then
begin
if try_to_consume(_FAR) then
is_far:=true
else if try_to_consume(_NEAR) then
is_far:=false;
end;
if (idtoken<>_NAME) and (token<>_SEMICOLON) then
begin
is_dll:=true;
dll_name:=get_stringconst;
if ExtractFileExt(dll_name)='' then
dll_name:=ChangeFileExt(dll_name,target_info.sharedlibext);
end;
if not(is_cdecl) and try_to_consume(_NAME) then
C_name:=get_stringconst;
consume(_SEMICOLON);
end;
{ export or public }
if idtoken in [_EXPORT,_PUBLIC] then
begin
consume(_ID);
if is_external_var then
Message(parser_e_not_external_and_export)
else
is_public_var:=true;
if try_to_consume(_NAME) then
C_name:=get_stringconst;
if (target_info.system in systems_allow_section_no_semicolon) and
(vs.typ=staticvarsym) and
try_to_consume (_SECTION) then
section_name:=get_stringconst;
consume(_SEMICOLON);
end;
{ Windows uses an indirect reference using import tables }
if is_dll and
(target_info.system in systems_all_windows) then
include(vs.varoptions,vo_is_dll_var);
{ This can only happen if vs.typ=staticvarsym }
if section_name<>'' then
begin
tstaticvarsym(vs).section:=section_name;
include(vs.varoptions,vo_has_section);
end;
{ Add C _ prefix }
if is_cdecl or
(
is_dll and
(target_info.system in systems_darwin)
) then
C_Name := target_info.Cprefix+C_Name;
if is_public_var then
begin
include(vs.varoptions,vo_is_public);
vs.varregable := vr_none;
{ mark as referenced }
inc(vs.refs);
end;
mangledname:=C_name;
{ now we can insert it in the import lib if its a dll, or
add it to the externals }
if is_external_var then
begin
if vo_is_typed_const in vs.varoptions then
Message(parser_e_initialized_not_for_external);
include(vs.varoptions,vo_is_external);
if is_far then
include(vs.varoptions,vo_is_far);
if (is_weak_external) then
begin
if not(target_info.system in systems_weak_linking) then
message(parser_e_weak_external_not_supported);
include(vs.varoptions,vo_is_weak_external);
end;
vs.varregable := vr_none;
if is_dll then
begin
if target_info.system in (systems_all_windows + systems_nativent +
[system_i386_emx, system_i386_os2]) then
mangledname:=make_dllmangledname(dll_name,C_name,0,pocall_none);
current_module.AddExternalImport(dll_name,C_Name,mangledname,0,true,false);
end
else
if tf_has_dllscanner in target_info.flags then
current_module.dllscannerinputlist.Add(vs.mangledname,vs);
end;
{ Set the assembler name }
tstaticvarsym(vs).set_mangledbasename(mangledname);
tstaticvarsym(vs).set_mangledname(mangledname);
end;
procedure try_consume_sectiondirective(var asection: ansistring);
begin
if idtoken=_SECTION then
begin
consume(_ID);
asection:=get_stringconst;
consume(_SEMICOLON);
end;
end;
procedure try_read_field_external(vs: tabstractvarsym);
var
extname: string;
begin
if try_to_consume(_EXTERNAL) then
begin
consume(_NAME);
extname:=get_stringconst;
tfieldvarsym(vs).set_externalname(extname);
consume(_SEMICOLON);
end;
end;
procedure try_read_field_external_sc(sc:TFPObjectList);
var
vs: tabstractvarsym;
begin
{ only allowed for one var }
vs:=tabstractvarsym(sc[0]);
if sc.count>1 then
Message1(parser_e_directive_only_one_var,arraytokeninfo[idtoken].str);
try_read_field_external(vs);
end;
procedure read_var_decls(options:Tvar_dec_options;out had_generic:boolean);
procedure read_default_value(sc : TFPObjectList);
var
vs : tabstractnormalvarsym;
tcsym : tstaticvarsym;
templist : tasmlist;
begin
vs:=tabstractnormalvarsym(sc[0]);
if sc.count>1 then
Message(parser_e_initialized_only_one_var);
if vo_is_thread_var in vs.varoptions then
Message(parser_e_initialized_not_for_threadvar);
consume(_EQ);
case vs.typ of
localvarsym :
begin
tcsym:=cstaticvarsym.create('$default'+vs.realname,vs_const,vs.vardef,[]);
include(tcsym.symoptions,sp_internal);
symtablestack.top.insertsym(tcsym);
templist:=tasmlist.create;
read_typed_const(templist,tcsym,false);
{ in case of a generic routine, this initialisation value is not
used, and will be re-parsed during specialisations (and the
current version is not type-correct and hence breaks code
generation for LLVM) }
if not parse_generic then
begin
vs.defaultconstsym:=tcsym;
current_asmdata.asmlists[al_typedconsts].concatlist(templist);
end;
templist.free;
end;
staticvarsym :
begin
maybe_guarantee_record_typesym(vs.vardef,vs.vardef.owner);
read_typed_const(current_asmdata.asmlists[al_typedconsts],tstaticvarsym(vs),false);
end;
else
internalerror(200611051);
end;
vs.varstate:=vs_initialised;
end;
{$ifdef gpc_mode}
procedure read_gpc_name(sc : TFPObjectList);
var
vs : tabstractnormalvarsym;
C_Name : string;
begin
consume(_ID);
C_Name:=get_stringconst;
vs:=tabstractnormalvarsym(sc[0]);
if sc.count>1 then
Message(parser_e_directive_only_one_var,'ABSOLUTE');
if vs.typ=staticvarsym then
begin
tstaticvarsym(vs).set_mangledname(C_Name);
include(vs.varoptions,vo_is_external);
end
else
Message(parser_e_no_local_var_external);
end;
{$endif}
procedure read_absolute(sc : TFPObjectList);
var
vs : tabstractvarsym;
abssym : tabsolutevarsym;
pt,hp : tnode;
st : tsymtable;
{$if defined(i386) or defined(i8086)}
tmpaddr : int64;
{$endif defined(i386) or defined(i8086)}
begin
abssym:=nil;
{ only allowed for one var }
vs:=tabstractvarsym(sc[0]);
if sc.count>1 then
Message1(parser_e_directive_only_one_var,'ABSOLUTE');
if vo_is_typed_const in vs.varoptions then
Message(parser_e_initialized_not_for_external);
{ parse the rest }
pt:=expr(true);
{ check allowed absolute types }
if (pt.nodetype=stringconstn) or
(is_constcharnode(pt)) then
begin
abssym:=cabsolutevarsym.create(vs.realname,vs.vardef);
abssym.fileinfo:=vs.fileinfo;
if pt.nodetype=stringconstn then
abssym.asmname:=stringdup(tstringconstnode(pt).asrawbytestring)
else
abssym.asmname:=stringdup(chr(tordconstnode(pt).value.svalue));
abssym.abstyp:=toasm;
end
{ address }
else if is_constintnode(pt) then
begin
abssym:=cabsolutevarsym.create(vs.realname,vs.vardef);
abssym.fileinfo:=vs.fileinfo;
abssym.abstyp:=toaddr;
{$ifndef cpu64bitaddr}
{ on 64 bit systems, abssym.addroffset is a qword and hence this
test is useless (value is a 64 bit entity) and will always fail
for positive values (since int64(high(abssym.addroffset))=-1
}
if (Tordconstnode(pt).value<int64(low(abssym.addroffset))) or
(Tordconstnode(pt).value>int64(high(abssym.addroffset))) then
message3(type_e_range_check_error_bounds,tostr(Tordconstnode(pt).value),tostr(low(abssym.addroffset)),tostr(high(abssym.addroffset)))
else
{$endif}
abssym.addroffset:=Tordconstnode(pt).value.svalue;
{$if defined(i386) or defined(i8086)}
tcpuabsolutevarsym(abssym).absseg:=false;
if (target_info.system in [system_i386_go32v2,system_i386_watcom,system_i8086_msdos,system_i8086_win16,system_i8086_embedded]) and
try_to_consume(_COLON) then
begin
pt.free;
pt:=expr(true);
if is_constintnode(pt) then
begin
{$if defined(i8086)}
tcpuabsolutevarsym(abssym).addrsegment:=abssym.addroffset;
tmpaddr:=tordconstnode(pt).value.svalue;
if (tmpaddr<int64(low(abssym.addroffset))) or
(tmpaddr>int64(high(abssym.addroffset))) then
message3(type_e_range_check_error_bounds,tostr(Tordconstnode(pt).value),tostr(low(abssym.addroffset)),tostr(high(abssym.addroffset)))
else
abssym.addroffset:=tmpaddr;
{$elseif defined(i386)}
tmpaddr:=abssym.addroffset shl 4+tordconstnode(pt).value.svalue;
if (tmpaddr<int64(low(abssym.addroffset))) or
(tmpaddr>int64(high(abssym.addroffset))) then
message3(type_e_range_check_error_bounds,tostr(Tordconstnode(pt).value),tostr(low(abssym.addroffset)),tostr(high(abssym.addroffset)))
else
abssym.addroffset:=tmpaddr;
{$endif}
tcpuabsolutevarsym(abssym).absseg:=true;
end
else
Message(type_e_ordinal_expr_expected);
end;
{$endif i386 or i8086}
end
{ variable }
else
begin
{ we have to be able to take the address of the absolute
expression
}
valid_for_addr(pt,true);
{ remove subscriptn before checking for loadn }
hp:=pt;
while (hp.nodetype in [subscriptn,typeconvn,vecn]) do
begin
{ check for implicit dereferencing and reject it }
if (hp.nodetype in [subscriptn,vecn]) then
begin
if (tunarynode(hp).left.resultdef.typ in [pointerdef,classrefdef]) then
break;
{ catch, e.g., 'var b: char absolute pchar_var[5];"
(pchar_var[5] is a pchar_2_string typeconv ->
the vecn only sees an array of char)
I don't know if all of these type conversions are
possible, but they're definitely all bad.
}
if (tunarynode(hp).left.nodetype=typeconvn) and
(ttypeconvnode(tunarynode(hp).left).convtype in
[tc_pchar_2_string,tc_pointer_2_array,
tc_intf_2_string,tc_intf_2_guid,
tc_dynarray_2_variant,tc_interface_2_variant,
tc_array_2_dynarray]) then
break;
if (tunarynode(hp).left.resultdef.typ=stringdef) and
not(tstringdef(tunarynode(hp).left.resultdef).stringtype in [st_shortstring,st_longstring]) then
break;
if (tunarynode(hp).left.resultdef.typ=objectdef) and
(tobjectdef(tunarynode(hp).left.resultdef).objecttype<>odt_object) then
break;
if is_dynamic_array(tunarynode(hp).left.resultdef) then
break;
end;
hp:=tunarynode(hp).left;
end;
if (hp.nodetype=loadn) then
begin
{ we should check the result type of loadn }
if not (tloadnode(hp).symtableentry.typ in [fieldvarsym,staticvarsym,localvarsym,paravarsym,absolutevarsym]) then
Message(parser_e_absolute_only_to_var_or_const);
if vs=tloadnode(hp).symtableentry then
Message(parser_e_absolute_sym_cannot_reference_itself)
else
begin
abssym:=cabsolutevarsym.create(vs.realname,vs.vardef);
abssym.fileinfo:=vs.fileinfo;
abssym.abstyp:=tovar;
abssym.ref:=node_to_propaccesslist(pt);
end;
{ if the sizes are different, can't be a regvar since you }
{ can't be "absolute upper 8 bits of a register" (except }
{ if its a record field of the same size of a record }
{ regvar, but in that case pt.resultdef.size will have }
{ the same size since it refers to the field and not to }
{ the whole record -- which is why we use pt and not hp) }
{ we can't take the size of an open array }
if is_open_array(pt.resultdef) or
(vs.vardef.size <> pt.resultdef.size) then
make_not_regable(pt,[ra_addr_regable]);
end
else
Message(parser_e_absolute_only_to_var_or_const);
end;
pt.free;
{ replace old varsym with the new absolutevarsym }
if assigned(abssym) then
begin
st:=vs.owner;
vs.owner.Deletesym(vs);
st.insertsym(abssym);
sc[0]:=abssym;
end;
end;
var
sc : TFPObjectList;
vs : tabstractvarsym;
hdef : tdef;
i : longint;
flags : thccflags;
first,
isgeneric,
semicoloneaten,
allowdefaultvalue,
hasdefaultvalue : boolean;
hintsymoptions : tsymoptions;
deprecatedmsg : pshortstring;
old_block_type : tblock_type;
sectionname : ansistring;
typepos,
tmp_filepos,
old_current_filepos : tfileposinfo;
begin
old_block_type:=block_type;
block_type:=bt_var;
{ Force an expected ID error message }
if not (token in [_ID,_CASE,_END]) then
consume(_ID);
{ read vars }
sc:=TFPObjectList.create(false);
first:=true;
had_generic:=false;
vs:=nil;
fillchar(tmp_filepos,sizeof(tmp_filepos),0);
while (token=_ID) do
begin
semicoloneaten:=false;
hasdefaultvalue:=false;
allowdefaultvalue:=true;
sc.clear;
repeat
if (token = _ID) then
begin
isgeneric:=(vd_check_generic in options) and
not (m_delphi in current_settings.modeswitches) and
(idtoken=_GENERIC);
case symtablestack.top.symtabletype of
localsymtable :
vs:=clocalvarsym.create(orgpattern,vs_value,generrordef,[]);
staticsymtable,
globalsymtable :
begin
vs:=cstaticvarsym.create(orgpattern,vs_value,generrordef,[]);
if vd_threadvar in options then
include(vs.varoptions,vo_is_thread_var);
end;
else
internalerror(200411064);
end;
sc.add(vs);
if isgeneric then
tmp_filepos:=current_filepos;
end
else
isgeneric:=false;
consume(_ID);
{ when the first variable had been read the next declaration could be
a "generic procedure", "generic function" or
"generic class (function/procedure)" }
if not first
and isgeneric
and (sc.count=1)
and (token in [_PROCEDURE,_FUNCTION,_CLASS]) then
begin
vs.free;
sc.clear;
had_generic:=true;
break;
end
else
begin
vs.register_sym;
if isgeneric then
begin
{ ensure correct error position }
old_current_filepos:=current_filepos;
current_filepos:=tmp_filepos;
symtablestack.top.insertsym(vs);
current_filepos:=old_current_filepos;
end
else
symtablestack.top.insertsym(vs);
end;
until not try_to_consume(_COMMA);
if had_generic then
break;
{ read variable type def }
block_type:=bt_var_type;
consume(_COLON);
typepos:=current_tokenpos;
{$ifdef gpc_mode}
if (m_gpc in current_settings.modeswitches) and
(token=_ID) and
(orgpattern='__asmname__') then
read_gpc_name(sc);
{$endif}
read_anon_type(hdef,false,nil);
maybe_guarantee_record_typesym(hdef,symtablestack.top);
for i:=0 to sc.count-1 do
begin
vs:=tabstractvarsym(sc[i]);
vs.vardef:=hdef;
end;
block_type:=bt_var;
{ Process procvar directives }
if maybe_parse_proc_directives(hdef) then
semicoloneaten:=true;
{ check for absolute }
if try_to_consume(_ABSOLUTE) then
begin
read_absolute(sc);
allowdefaultvalue:=false;
end;
{ Check for EXTERNAL etc directives before a semicolon }
if (idtoken in [_EXPORT,_EXTERNAL,_PUBLIC,_CVAR]) or (idtoken = _WEAKEXTERNAL) then
begin
read_public_and_external_sc(sc);
allowdefaultvalue:=false;
semicoloneaten:=true;
end;
{ try to parse the hint directives }
hintsymoptions:=[];
deprecatedmsg:=nil;
try_consume_hintdirective(hintsymoptions,deprecatedmsg);
for i:=0 to sc.count-1 do
begin
vs:=tabstractvarsym(sc[i]);
vs.symoptions := vs.symoptions + hintsymoptions;
if deprecatedmsg<>nil then
vs.deprecatedmsg:=stringdup(deprecatedmsg^);
end;
stringdispose(deprecatedmsg);
{ Handling of Delphi typed const = initialized vars }
if allowdefaultvalue and
(token=_EQ) and
not(m_tp7 in current_settings.modeswitches) and
(symtablestack.top.symtabletype<>parasymtable) then
begin
{ Add calling convention for procvar }
if (
(hdef.typ=procvardef) or
is_funcref(hdef)
) and
(hdef.typesym=nil) then
begin
if po_is_function_ref in tprocvardef(hdef).procoptions then
begin
if not (m_function_references in current_settings.modeswitches) and
not (po_is_block in tprocvardef(hdef).procoptions) then
messagepos(typepos,sym_e_error_in_type_def)
else
begin
if adjust_funcref(hdef,nil,nil) then
{ the def was changed, so update it }
for i:=0 to sc.count-1 do
begin
vs:=tabstractvarsym(sc[i]);
vs.vardef:=hdef;
end;
if current_scanner.replay_stack_depth=0 then
hdef.register_def;
end;
end;
handle_calling_convention(hdef,hcc_default_actions_intf);
end;
read_default_value(sc);
hasdefaultvalue:=true;
end
else
begin
if not(semicoloneaten) then
consume(_SEMICOLON);
end;
{ Support calling convention for procvars after semicolon }
if not(hasdefaultvalue) and
(
(hdef.typ=procvardef) or
is_funcref(hdef)
) and
(hdef.typesym=nil) then
begin
{ Parse procvar directives after ; }
maybe_parse_proc_directives(hdef);
if (hdef.typ=procvardef) and (po_is_function_ref in tprocvardef(hdef).procoptions) then
begin
if not (m_function_references in current_settings.modeswitches) and
not (po_is_block in tprocvardef(hdef).procoptions) then
messagepos(typepos,sym_e_error_in_type_def)
else
begin
if adjust_funcref(hdef,nil,nil) then
{ the def was changed, so update it }
for i:=0 to sc.count-1 do
begin
vs:=tabstractvarsym(sc[i]);
vs.vardef:=hdef;
end;
if current_scanner.replay_stack_depth=0 then
hdef.register_def;
end;
end;
{ Add calling convention for procvar }
if hdef.typ=procvardef then
flags:=hcc_default_actions_intf
else
flags:=hcc_default_actions_intf_struct;
handle_calling_convention(hdef,flags);
{ Handling of Delphi typed const = initialized vars }
if (token=_EQ) and
not(m_tp7 in current_settings.modeswitches) and
(symtablestack.top.symtabletype<>parasymtable) then
begin
read_default_value(sc);
hasdefaultvalue:=true;
end;
end;
{ Check for EXTERNAL etc directives or, in macpas, if cs_external_var is set}
if (
(
((idtoken in [_EXPORT,_EXTERNAL,_PUBLIC,_CVAR]) or (idtoken = _WEAKEXTERNAL)) and
(m_cvar_support in current_settings.modeswitches)
) or
(
(m_mac in current_settings.modeswitches) and
(
(cs_external_var in current_settings.localswitches) or
(cs_externally_visible in current_settings.localswitches)
)
)
) then
read_public_and_external_sc(sc);
{ try to parse a section directive }
if (target_info.system in systems_allow_section) and
(symtablestack.top.symtabletype in [staticsymtable,globalsymtable]) and
(idtoken=_SECTION) then
begin
try_consume_sectiondirective(sectionname);
if sectionname<>'' then
begin
for i:=0 to sc.count-1 do
begin
vs:=tabstractvarsym(sc[i]);
if (vs.varoptions *[vo_is_external,vo_is_weak_external])<>[] then
Message(parser_e_externals_no_section);
if vs.typ<>staticvarsym then
Message(parser_e_section_no_locals);
tstaticvarsym(vs).section:=sectionname;
include(vs.varoptions, vo_has_section);
end;
end;
end;
{ allocate normal variable (non-external and non-typed-const) staticvarsyms }
for i:=0 to sc.count-1 do
begin
vs:=tabstractvarsym(sc[i]);
if (vs.typ=staticvarsym) and
not(vo_is_typed_const in vs.varoptions) and
not(vo_is_external in vs.varoptions) then
cnodeutils.insertbssdata(tstaticvarsym(vs));
if vo_is_public in vs.varoptions then
current_module.add_public_asmsym(vs.mangledname,AB_GLOBAL,AT_DATA);
end;
first:=false;
end;
block_type:=old_block_type;
{ free the list }
sc.free;
end;
function check_allowed_for_var_or_const(def:tdef;allowdynarray:boolean):boolean;
var
stowner,tmpdef : tdef;
st : tsymtable;
begin
result:=true;
st:=symtablestack.top;
if not (st.symtabletype in [recordsymtable,objectsymtable]) then
exit;
stowner:=tdef(st.defowner);
while assigned(stowner) and (stowner.typ in [objectdef,recorddef]) do
begin
if def.typ=arraydef then
begin
tmpdef:=def;
while (tmpdef.typ=arraydef) do
begin
{ dynamic arrays are allowed in certain cases }
if allowdynarray and (ado_IsDynamicArray in tarraydef(tmpdef).arrayoptions) then
begin
tmpdef:=nil;
break;
end;
tmpdef:=tarraydef(tmpdef).elementdef;
end;
end
else
tmpdef:=def;
if assigned(tmpdef) and
(is_object(tmpdef) or is_record(tmpdef)) and
is_owned_by(tabstractrecorddef(stowner),tabstractrecorddef(tmpdef)) then
begin
Message1(type_e_type_is_not_completly_defined,tabstractrecorddef(tmpdef).RttiName);
result:=false;
break;
end;
stowner:=tdef(stowner.owner.defowner);
end;
end;
procedure read_record_fields(options:Tvar_dec_options; reorderlist: TFPObjectList; variantdesc : ppvariantrecdesc;out had_generic:boolean; out attr_element_count : integer);
var
sc : TFPObjectList;
i : longint;
hs,sorg : string;
gendef,hdef,casetype : tdef;
{ maxsize contains the max. size of a variant }
{ startvarrec contains the start of the variant part of a record }
maxsize, startvarrecsize : asizeint;
usedalign,
maxalignment,startvarrecalign,
maxpadalign, startpadalign: shortint;
pt : tnode;
fieldvs : tfieldvarsym;
hstaticvs : tstaticvarsym;
vs : tabstractvarsym;
srsym : tsym;
srsymtable : TSymtable;
visibility : tvisibility;
recst : tabstractrecordsymtable;
unionsymtable : trecordsymtable;
offset : longint;
uniondef : trecorddef;
hintsymoptions : tsymoptions;
deprecatedmsg : pshortstring;
hadgendummy,
semicoloneaten,
removeclassoption: boolean;
dummyattrelementcount : integer;
{$if defined(powerpc) or defined(powerpc64)}
tempdef: tdef;
is_first_type: boolean;
{$endif powerpc or powerpc64}
old_block_type: tblock_type;
typepos : tfileposinfo;
begin
old_block_type:=block_type;
block_type:=bt_var;
recst:=tabstractrecordsymtable(symtablestack.top);
{$if defined(powerpc) or defined(powerpc64)}
is_first_type:=true;
{$endif powerpc or powerpc64}
{ Force an expected ID error message }
if not (token in [_ID,_CASE,_END]) then
consume(_ID);
{ read vars }
sc:=TFPObjectList.create(false);
removeclassoption:=false;
had_generic:=false;
attr_element_count:=0;
while (token=_ID) and
not(((vd_object in options) or
((vd_record in options) and (m_advanced_records in current_settings.modeswitches))) and
((idtoken in [_PUBLIC,_PRIVATE,_PUBLISHED,_PROTECTED,_STRICT]) or
((m_final_fields in current_settings.modeswitches) and
(idtoken=_FINAL)))) do
begin
visibility:=symtablestack.top.currentvisibility;
semicoloneaten:=false;
sc.clear;
repeat
sorg:=orgpattern;
if token=_ID then
begin
vs:=cfieldvarsym.create(sorg,vs_value,generrordef,[]);
{ normally the visibility is set via addfield, but sometimes
we collect symbols so we can add them in a batch of
potentially mixed visibility, and then the individual
symbols need to have their visibility already set }
vs.visibility:=visibility;
if (vd_check_generic in options) and (idtoken=_GENERIC) then
had_generic:=true;
end
else
vs:=nil;
consume(_ID);
if assigned(vs) and
(
not had_generic or
not (token in [_PROCEDURE,_FUNCTION,_CLASS])
) then
begin
vs.register_sym;
sc.add(vs);
recst.insertsym(vs);
had_generic:=false;
end
else
vs.free;
until not try_to_consume(_COMMA);
if m_delphi in current_settings.modeswitches then
block_type:=bt_var_type
else
block_type:=old_block_type;
if had_generic and (sc.count=0) then
break;
consume(_COLON);
if attr_element_count=0 then
attr_element_count:=sc.Count;
typepos:=current_filepos;
{ make sure that the correct genericdef is set up, especially if
we're dealing with anonymous type declarations }
gendef:=nil;
if df_specialization in current_structdef.defoptions then
begin
srsymtable:=current_structdef.genericdef.getsymtable(gs_record);
if not assigned(srsymtable) then
internalerror(2024041204);
srsym:=tsym(srsymtable.find(tabstractvarsym(sc[0]).name));
if not assigned(srsym) then
internalerror(2024041205);
if srsym.typ<>fieldvarsym then
internalerror(2024041206);
gendef:=tfieldvarsym(srsym).vardef;
end;
read_anon_type(hdef,false,tstoreddef(gendef));
maybe_guarantee_record_typesym(hdef,symtablestack.top);
{$ifdef wasm}
if is_wasm_reference_type(hdef) then
messagepos(typepos,sym_e_wasm_ref_types_cannot_be_used_in_records);
{$endif wasm}
block_type:=bt_var;
{ allow only static fields reference to struct where they are declared }
if not (vd_class in options) then
begin
if not check_allowed_for_var_or_const(hdef,true) then
{ for error recovery or compiler will crash later }
hdef:=generrordef;
end;
{ field type is a generic param so set a flag in the struct }
if assigned(hdef.typesym) and (sp_generic_para in hdef.typesym.symoptions) then
include(current_structdef.defoptions,df_has_generic_fields);
{ Process procvar directives }
if maybe_parse_proc_directives(hdef) then
semicoloneaten:=true;
{$if defined(powerpc) or defined(powerpc64)}
{ from gcc/gcc/config/rs6000/rs6000.h:
/* APPLE LOCAL begin Macintosh alignment 2002-1-22 ff */
/* Return the alignment of a struct based on the Macintosh PowerPC
alignment rules. In general the alignment of a struct is
determined by the greatest alignment of its elements. However, the
PowerPC rules cause the alignment of a struct to peg at word
alignment except when the first field has greater than word
(32-bit) alignment, in which case the alignment is determined by
the alignment of the first field. */
}
{ TODO: check whether this is also for AIX }
if (target_info.abi in [abi_powerpc_aix,abi_powerpc_darwin]) and
is_first_type and
(symtablestack.top.symtabletype=recordsymtable) and
(trecordsymtable(symtablestack.top).usefieldalignment=C_alignment) then
begin
tempdef:=hdef;
while tempdef.typ=arraydef do
tempdef:=tarraydef(tempdef).elementdef;
if tempdef.typ<>recorddef then
maxpadalign:=tempdef.alignment
else
maxpadalign:=trecorddef(tempdef).padalignment;
if (maxpadalign>4) and
(maxpadalign>trecordsymtable(symtablestack.top).padalignment) then
trecordsymtable(symtablestack.top).padalignment:=maxpadalign;
is_first_type:=false;
end;
{$endif powerpc or powerpc64}
{ types that use init/final are not allowed in variant parts, but
classes are allowed }
if (variantrecordlevel>0) then
if is_managed_type(hdef) then
Message(parser_e_cant_use_inittable_here);
{ try to parse the hint directives }
hintsymoptions:=[];
deprecatedmsg:=nil;
try_consume_hintdirective(hintsymoptions,deprecatedmsg);
{ update variable type and hints }
for i:=0 to sc.count-1 do
begin
fieldvs:=tfieldvarsym(sc[i]);
fieldvs.vardef:=hdef;
{ insert any additional hint directives }
fieldvs.symoptions := fieldvs.symoptions + hintsymoptions;
if deprecatedmsg<>nil then
fieldvs.deprecatedmsg:=stringdup(deprecatedmsg^);
end;
stringdispose(deprecatedmsg);
{ Records and objects can't have default values }
{ for a record there doesn't need to be a ; before the END or ) }
if not(token in [_END,_RKLAMMER]) and
not(semicoloneaten) then
consume(_SEMICOLON);
{ Parse procvar directives after ; }
maybe_parse_proc_directives(hdef);
{ Add calling convention for procvar }
if (
(hdef.typ=procvardef) or
is_funcref(hdef)
) and (hdef.typesym=nil) then
begin
if (hdef.typ=procvardef) and (po_is_function_ref in tprocvardef(hdef).procoptions) then
begin
if not (m_function_references in current_settings.modeswitches) and
not (po_is_block in tprocvardef(hdef).procoptions) then
messagepos(typepos,sym_e_error_in_type_def)
else
begin
if adjust_funcref(hdef,nil,nil) then
{ the def was changed, so update it }
for i:=0 to sc.count-1 do
begin
fieldvs:=tfieldvarsym(sc[i]);
fieldvs.vardef:=hdef;
end;
if current_scanner.replay_stack_depth=0 then
hdef.register_def;
end;
end;
handle_calling_convention(hdef,hcc_default_actions_intf);
end;
if (vd_object in options) then
begin
{ if it is not a class var section and token=STATIC then it is a class field too }
if not (vd_class in options) and try_to_consume(_STATIC) then
begin
consume(_SEMICOLON);
include(options,vd_class);
removeclassoption:=true;
end;
{ Fields in Java classes/interfaces can have a separately
specified external name }
if is_java_class_or_interface(tdef(recst.defowner)) and
(oo_is_external in tobjectdef(recst.defowner).objectoptions) then
try_read_field_external_sc(sc);
end;
if (visibility=vis_published) and
not(is_class(hdef)) then
begin
MessagePos(tfieldvarsym(sc[0]).fileinfo,parser_e_cant_publish_that);
visibility:=vis_public;
end;
if (visibility=vis_published) and
not(oo_can_have_published in tobjectdef(hdef).objectoptions) and
not(m_delphi in current_settings.modeswitches) then
begin
MessagePos(tfieldvarsym(sc[0]).fileinfo,parser_e_only_publishable_classes_can_be_published);
visibility:=vis_public;
end;
if vd_class in options then
begin
{ add static flag and staticvarsyms }
for i:=0 to sc.count-1 do
begin
fieldvs:=tfieldvarsym(sc[i]);
fieldvs.visibility:=visibility;
hstaticvs:=make_field_static(recst,fieldvs);
if vd_threadvar in options then
include(hstaticvs.varoptions,vo_is_thread_var);
if not parse_generic then
cnodeutils.insertbssdata(hstaticvs);
if vd_final in options then
hstaticvs.varspez:=vs_final;
end;
if removeclassoption then
begin
exclude(options,vd_class);
removeclassoption:=false;
end;
end;
if vd_final in options then
begin
{ add final flag }
for i:=0 to sc.count-1 do
begin
fieldvs:=tfieldvarsym(sc[i]);
fieldvs.varspez:=vs_final;
end;
end;
if not(vd_canreorder in options) then
{ add field(s) to the recordsymtable }
recst.addfieldlist(sc,false)
else
{ we may reorder the fields before adding them to the symbol
table }
reorderlist.concatlistcopy(sc)
end;
if m_delphi in current_settings.modeswitches then
block_type:=bt_var_type
else
block_type:=old_block_type;
{ Check for Case }
if (vd_record in options) and
try_to_consume(_CASE) then
begin
maxsize:=0;
maxalignment:=0;
maxpadalign:=0;
{ already inside a variant record? if not, setup a new variantdesc chain }
if not(assigned(variantdesc)) then
variantdesc:=@trecorddef(trecordsymtable(recst).defowner).variantrecdesc;
{ else just concat the info to the given one }
new(variantdesc^);
fillchar(variantdesc^^,sizeof(tvariantrecdesc),0);
{ including a field declaration? }
fieldvs:=nil;
if token=_ID then
begin
sorg:=orgpattern;
hs:=pattern;
searchsym(hs,srsym,srsymtable);
if not(assigned(srsym) and (srsym.typ in [typesym,unitsym])) then
begin
consume(_ID);
consume(_COLON);
fieldvs:=cfieldvarsym.create(sorg,vs_value,generrordef,[]);
variantdesc^^.variantselector:=fieldvs;
symtablestack.top.insertsym(fieldvs);
end;
end;
read_anon_type(casetype,true,nil);
block_type:=bt_var;
if assigned(fieldvs) then
begin
fieldvs.vardef:=casetype;
recst.addfield(fieldvs,recst.currentvisibility);
end;
if not(is_ordinal(casetype))
{$ifndef cpu64bitaddr}
or is_64bitint(casetype)
{$endif cpu64bitaddr}
then
Message(type_e_ordinal_expr_expected);
consume(_OF);
UnionSymtable:=trecordsymtable.create('',current_settings.packrecords,current_settings.alignment.recordalignmin);
UnionDef:=crecorddef.create('',unionsymtable);
uniondef.isunion:=true;
startvarrecsize:=UnionSymtable.datasize;
{ align the bitpacking to the next byte }
UnionSymtable.datasize:=startvarrecsize;
startvarrecalign:=UnionSymtable.fieldalignment;
startpadalign:=Unionsymtable.padalignment;
symtablestack.push(UnionSymtable);
repeat
SetLength(variantdesc^^.branches,length(variantdesc^^.branches)+1);
fillchar(variantdesc^^.branches[high(variantdesc^^.branches)],
sizeof(variantdesc^^.branches[high(variantdesc^^.branches)]),0);
repeat
pt:=comp_expr([ef_accept_equal]);
if not(pt.nodetype=ordconstn) then
Message(parser_e_illegal_expression);
inserttypeconv(pt,casetype);
{ iso pascal does not support ranges in variant record definitions }
if (([m_iso,m_extpas]*current_settings.modeswitches)=[]) and try_to_consume(_POINTPOINT) then
pt:=crangenode.create(pt,comp_expr([ef_accept_equal]))
else
begin
with variantdesc^^.branches[high(variantdesc^^.branches)] do
begin
SetLength(values,length(values)+1);
values[high(values)]:=tordconstnode(pt).value;
end;
end;
pt.free;
if token=_COMMA then
consume(_COMMA)
else
break;
until false;
if m_delphi in current_settings.modeswitches then
block_type:=bt_var_type
else
block_type:=old_block_type;
consume(_COLON);
{ read the vars }
consume(_LKLAMMER);
inc(variantrecordlevel);
if token<>_RKLAMMER then
read_record_fields([vd_record],nil,@variantdesc^^.branches[high(variantdesc^^.branches)].nestedvariant,hadgendummy,dummyattrelementcount);
dec(variantrecordlevel);
consume(_RKLAMMER);
{ calculates maximal variant size }
maxsize:=max(maxsize,unionsymtable.datasize);
maxalignment:=max(maxalignment,unionsymtable.fieldalignment);
maxpadalign:=max(maxpadalign,unionsymtable.padalignment);
{ the items of the next variant are overlayed }
unionsymtable.datasize:=startvarrecsize;
unionsymtable.fieldalignment:=startvarrecalign;
unionsymtable.padalignment:=startpadalign;
if (token<>_END) and (token<>_RKLAMMER) then
consume(_SEMICOLON)
else
break;
until (token=_END) or (token=_RKLAMMER);
symtablestack.pop(UnionSymtable);
{ at last set the record size to that of the biggest variant }
unionsymtable.datasize:=maxsize;
unionsymtable.fieldalignment:=maxalignment;
unionsymtable.addalignmentpadding;
{$if defined(powerpc) or defined(powerpc64)}
{ parent inherits the alignment padding if the variant is the first "field" of the parent record/variant }
if (target_info.system in [system_powerpc_darwin, system_powerpc_macosclassic, system_powerpc64_darwin]) and
is_first_type and
(recst.usefieldalignment=C_alignment) and
(maxpadalign>recst.padalignment) then
recst.padalignment:=maxpadalign;
{$endif powerpc or powerpc64}
{ Align the offset where the union symtable is added }
case recst.usefieldalignment of
{ allow the unionsymtable to be aligned however it wants }
{ (within the global min/max limits) }
0, { default }
C_alignment:
usedalign:=used_align(unionsymtable.recordalignment,current_settings.alignment.recordalignmin,current_settings.alignment.maxCrecordalign);
{ 1 byte alignment if we are bitpacked }
bit_alignment:
usedalign:=1;
mac68k_alignment:
usedalign:=2;
{ otherwise alignment at the packrecords alignment of the }
{ current record }
else
usedalign:=used_align(recst.fieldalignment,current_settings.alignment.recordalignmin,current_settings.alignment.recordalignmax);
end;
offset:=align(recst.datasize,usedalign);
recst.datasize:=offset+unionsymtable.datasize;
if unionsymtable.recordalignment>recst.fieldalignment then
recst.fieldalignment:=unionsymtable.recordalignment;
trecordsymtable(recst).insertunionst(Unionsymtable,offset);
uniondef.owner.deletedef(uniondef);
end;
{ free the list }
sc.free;
{$ifdef powerpc}
is_first_type := false;
{$endif powerpc}
block_type:=old_block_type;
end;
end.