Pārlūkot izejas kodu

* synchronised with trunk till r40038

git-svn-id: branches/debug_eh@40643 -
Jonas Maebe 6 gadi atpakaļ
vecāks
revīzija
c7d701d117

+ 1 - 0
.gitattributes

@@ -16411,6 +16411,7 @@ tests/webtbs/tw33607.pp svneol=native#text/plain
 tests/webtbs/tw33635.pp svneol=native#text/pascal
 tests/webtbs/tw33635.pp svneol=native#text/pascal
 tests/webtbs/tw3364.pp svneol=native#text/plain
 tests/webtbs/tw3364.pp svneol=native#text/plain
 tests/webtbs/tw3366.pp svneol=native#text/plain
 tests/webtbs/tw3366.pp svneol=native#text/plain
+tests/webtbs/tw33666.pp svneol=native#text/plain
 tests/webtbs/tw33696.pp svneol=native#text/pascal
 tests/webtbs/tw33696.pp svneol=native#text/pascal
 tests/webtbs/tw33700.pp svneol=native#text/pascal
 tests/webtbs/tw33700.pp svneol=native#text/pascal
 tests/webtbs/tw33706.pp svneol=native#text/plain
 tests/webtbs/tw33706.pp svneol=native#text/plain

+ 6 - 17
compiler/hlcgobj.pas

@@ -5182,7 +5182,7 @@ implementation
 
 
   procedure thlcgobj.gen_load_return_value(list: TAsmList);
   procedure thlcgobj.gen_load_return_value(list: TAsmList);
     var
     var
-      ressym : tabstractnormalvarsym;
+      ressym : tsym;
       retdef : tdef;
       retdef : tdef;
     begin
     begin
       { Is the loading needed? }
       { Is the loading needed? }
@@ -5196,30 +5196,19 @@ implementation
         exit;
         exit;
 
 
       { constructors return self }
       { constructors return self }
-      if (current_procinfo.procdef.proctypeoption=potype_constructor) then
-        begin
-          ressym:=tabstractnormalvarsym(current_procinfo.procdef.parast.Find('self'));
-          retdef:=ressym.vardef;
-          { and TP-style constructors return a pointer to self }
-          if is_object(ressym.vardef) then
-            retdef:=cpointerdef.getreusable(retdef);
-        end
-      else
-        begin
-          ressym:=tabstractnormalvarsym(current_procinfo.procdef.funcretsym);
-          retdef:=ressym.vardef;
-        end;
+      if not current_procinfo.procdef.getfuncretsyminfo(ressym,retdef) then
+        internalerror(2018122501);
       if (ressym.refs>0) or
       if (ressym.refs>0) or
          is_managed_type(retdef) then
          is_managed_type(retdef) then
         begin
         begin
           { was: don't do anything if funcretloc.loc in [LOC_INVALID,LOC_REFERENCE] }
           { was: don't do anything if funcretloc.loc in [LOC_INVALID,LOC_REFERENCE] }
           if not paramanager.ret_in_param(current_procinfo.procdef.returndef,current_procinfo.procdef) then
           if not paramanager.ret_in_param(current_procinfo.procdef.returndef,current_procinfo.procdef) then
-            gen_load_loc_function_result(list,retdef,ressym.localloc);
+            gen_load_loc_function_result(list,retdef,tabstractnormalvarsym(ressym).localloc);
         end
         end
       else
       else
         gen_load_uninitialized_function_result(list,current_procinfo.procdef,retdef,current_procinfo.procdef.funcretloc[calleeside]);
         gen_load_uninitialized_function_result(list,current_procinfo.procdef,retdef,current_procinfo.procdef.funcretloc[calleeside]);
-      if ressym.localloc.loc=LOC_REFERENCE then
-        tg.UnGetLocal(list,ressym.localloc.reference);
+      if tabstractnormalvarsym(ressym).localloc.loc=LOC_REFERENCE then
+        tg.UnGetLocal(list,tabstractnormalvarsym(ressym).localloc.reference);
     end;
     end;
 
 
   procedure thlcgobj.gen_stack_check_size_para(list: TAsmList);
   procedure thlcgobj.gen_stack_check_size_para(list: TAsmList);

+ 10 - 0
compiler/jvm/symcpu.pas

@@ -109,6 +109,7 @@ type
     exprasmlist      : TAsmList;
     exprasmlist      : TAsmList;
     function  jvmmangledbasename(signature: boolean): TSymStr;
     function  jvmmangledbasename(signature: boolean): TSymStr;
     function mangledname: TSymStr; override;
     function mangledname: TSymStr; override;
+    function getfuncretsyminfo(out ressym: tsym; out resdef: tdef): boolean; override;
     destructor destroy; override;
     destructor destroy; override;
   end;
   end;
   tcpuprocdefclass = class of tcpuprocdef;
   tcpuprocdefclass = class of tcpuprocdef;
@@ -751,6 +752,15 @@ implementation
         result:=_mangledname;
         result:=_mangledname;
     end;
     end;
 
 
+  function tcpuprocdef.getfuncretsyminfo(out ressym: tsym; out resdef: tdef): boolean;
+    begin
+      { constructors don't have a result on the JVM platform }
+      if proctypeoption<>potype_constructor then
+        result:=inherited
+      else
+        result:=false;
+    end;
+
 
 
   destructor tcpuprocdef.destroy;
   destructor tcpuprocdef.destroy;
     begin
     begin

+ 11 - 11
compiler/ncal.pas

@@ -4627,6 +4627,17 @@ implementation
 
 
     function tcallnode.paraneedsinlinetemp(para: tcallparanode; const pushconstaddr, complexpara: boolean): boolean;
     function tcallnode.paraneedsinlinetemp(para: tcallparanode; const pushconstaddr, complexpara: boolean): boolean;
       begin
       begin
+        { We don't need temps for parameters that are already temps, except if
+          the passed temp could be put in a regvar while the parameter inside
+          the routine cannot be (e.g., because its address is taken in the
+          routine), or if the temp is a const and the parameter gets modified }
+        if (para.left.nodetype=temprefn) and
+           (not(ti_may_be_in_reg in ttemprefnode(para.left).tempflags) or
+            not(tparavarsym(para.parasym).varregable in [vr_none,vr_addr])) and
+           (not(ti_const in ttemprefnode(para.left).tempflags) or
+            (tparavarsym(para.parasym).varstate in [vs_initialised,vs_declared,vs_read])) then
+          exit(false);
+
         { We need a temp if the passed value will not be in memory, while
         { We need a temp if the passed value will not be in memory, while
           the parameter inside the routine must be in memory }
           the parameter inside the routine must be in memory }
         if (tparavarsym(para.parasym).varregable in [vr_none,vr_addr]) and
         if (tparavarsym(para.parasym).varregable in [vr_none,vr_addr]) and
@@ -4760,17 +4771,6 @@ implementation
            )
            )
           );
           );
 
 
-        { We don't need temps for parameters that are already temps, except if
-          the passed temp could be put in a regvar while the parameter inside
-          the routine cannot be (e.g., because its address is taken in the
-          routine), or if the temp is a const and the parameter gets modified }
-        if (para.left.nodetype=temprefn) and
-           (not(ti_may_be_in_reg in ttemprefnode(para.left).tempflags) or
-            not(tparavarsym(para.parasym).varregable in [vr_none,vr_addr])) and
-           (not(ti_const in ttemprefnode(para.left).tempflags) or
-            (tparavarsym(para.parasym).varstate in [vs_initialised,vs_declared,vs_read])) then
-          exit;
-
         { check if we have to create a temp, assign the parameter's
         { check if we have to create a temp, assign the parameter's
           contents to that temp and then substitute the parameter
           contents to that temp and then substitute the parameter
           with the temp everywhere in the function                  }
           with the temp everywhere in the function                  }

+ 3 - 0
compiler/ncon.pas

@@ -982,6 +982,7 @@ implementation
                             Message1(option_code_page_not_available,IntToStr(cp1));
                             Message1(option_code_page_not_available,IntToStr(cp1));
                           initwidestring(pw);
                           initwidestring(pw);
                           setlengthwidestring(pw,len);
                           setlengthwidestring(pw,len);
+                          { returns room for terminating 0 }
                           l:=Utf8ToUnicode(PUnicodeChar(pw^.data),len,value_str,len);
                           l:=Utf8ToUnicode(PUnicodeChar(pw^.data),len,value_str,len);
                           if (l<>getlengthwidestring(pw)) then
                           if (l<>getlengthwidestring(pw)) then
                             begin
                             begin
@@ -989,6 +990,7 @@ implementation
                               ReAllocMem(value_str,l);
                               ReAllocMem(value_str,l);
                             end;
                             end;
                           unicode2ascii(pw,value_str,cp1);
                           unicode2ascii(pw,value_str,cp1);
+                          len:=l-1;
                           donewidestring(pw);
                           donewidestring(pw);
                         end
                         end
                       else
                       else
@@ -1000,6 +1002,7 @@ implementation
                           initwidestring(pw);
                           initwidestring(pw);
                           setlengthwidestring(pw,len);
                           setlengthwidestring(pw,len);
                           ascii2unicode(value_str,len,cp2,pw);
                           ascii2unicode(value_str,len,cp2,pw);
+                          { returns room for terminating 0 }
                           l:=UnicodeToUtf8(nil,0,PUnicodeChar(pw^.data),len);
                           l:=UnicodeToUtf8(nil,0,PUnicodeChar(pw^.data),len);
                           if l<>len then
                           if l<>len then
                             ReAllocMem(value_str,l);
                             ReAllocMem(value_str,l);

+ 8 - 10
compiler/nflw.pas

@@ -1776,8 +1776,9 @@ implementation
 
 
     function texitnode.pass_typecheck:tnode;
     function texitnode.pass_typecheck:tnode;
       var
       var
-        pd: tprocdef;
         newstatement : tstatementnode;
         newstatement : tstatementnode;
+        ressym: tsym;
+        resdef: tdef;
       begin
       begin
         result:=nil;
         result:=nil;
         newstatement:=nil;
         newstatement:=nil;
@@ -1793,16 +1794,13 @@ implementation
           because the code to this that we add in tnodeutils.wrap_proc_body()
           because the code to this that we add in tnodeutils.wrap_proc_body()
           gets inserted before the exit label to which this node will jump }
           gets inserted before the exit label to which this node will jump }
         if (target_info.system in systems_fpnestedstruct) and
         if (target_info.system in systems_fpnestedstruct) and
-           not(nf_internal in flags) then
+           not(nf_internal in flags) and
+           current_procinfo.procdef.getfuncretsyminfo(ressym,resdef) and
+           (tabstractnormalvarsym(ressym).inparentfpstruct) then
           begin
           begin
-            pd:=current_procinfo.procdef;
-            if assigned(pd.funcretsym) and
-               tabstractnormalvarsym(pd.funcretsym).inparentfpstruct then
-              begin
-                if not assigned(result) then
-                  result:=internalstatements(newstatement);
-                cnodeutils.load_parentfpstruct_nested_funcret(current_procinfo.procdef,newstatement);
-              end;
+            if not assigned(result) then
+              result:=internalstatements(newstatement);
+            cnodeutils.load_parentfpstruct_nested_funcret(ressym,newstatement);
           end;
           end;
         if assigned(result) then
         if assigned(result) then
           begin
           begin

+ 13 - 13
compiler/ngenutil.pas

@@ -74,7 +74,7 @@ interface
         the value to be returned; replacing it with an absolutevarsym that
         the value to be returned; replacing it with an absolutevarsym that
         redirects to the field in the parentfpstruct doesn't work, as the code
         redirects to the field in the parentfpstruct doesn't work, as the code
         generator cannot deal with such symbols }
         generator cannot deal with such symbols }
-       class procedure load_parentfpstruct_nested_funcret(pd: tprocdef; var stat: tstatementnode);
+       class procedure load_parentfpstruct_nested_funcret(ressym: tsym; var stat: tstatementnode);
       { called after parsing a routine with the code of the entire routine
       { called after parsing a routine with the code of the entire routine
         as argument; can be used to modify the node tree. By default handles
         as argument; can be used to modify the node tree. By default handles
         insertion of code for systems that perform the typed constant
         insertion of code for systems that perform the typed constant
@@ -584,17 +584,17 @@ implementation
     end;
     end;
 
 
 
 
-  class procedure tnodeutils.load_parentfpstruct_nested_funcret(pd: tprocdef; var stat: tstatementnode);
+  class procedure tnodeutils.load_parentfpstruct_nested_funcret(ressym: tsym; var stat: tstatementnode);
     var
     var
       target: tnode;
       target: tnode;
     begin
     begin
-      target:=cloadnode.create(pd.funcretsym, pd.funcretsym.owner);
+      target:=cloadnode.create(ressym, ressym.owner);
       { ensure the target of this assignment doesn't translate the
       { ensure the target of this assignment doesn't translate the
         funcretsym also to its alias in the parentfpstruct }
         funcretsym also to its alias in the parentfpstruct }
       include(target.flags, nf_internal);
       include(target.flags, nf_internal);
       addstatement(stat,
       addstatement(stat,
         cassignmentnode.create(
         cassignmentnode.create(
-          target, cloadnode.create(pd.funcretsym, pd.funcretsym.owner)
+          target, cloadnode.create(ressym, ressym.owner)
         )
         )
       );
       );
     end;
     end;
@@ -604,7 +604,9 @@ implementation
     var
     var
       stat: tstatementnode;
       stat: tstatementnode;
       block: tnode;
       block: tnode;
+      ressym,
       psym: tsym;
       psym: tsym;
+      resdef: tdef;
     begin
     begin
       result:=maybe_insert_trashing(pd,n);
       result:=maybe_insert_trashing(pd,n);
 
 
@@ -674,16 +676,14 @@ implementation
             end;
             end;
           end;
           end;
         end;
         end;
-      if target_info.system in systems_fpnestedstruct then
+      if (target_info.system in systems_fpnestedstruct) and
+         pd.getfuncretsyminfo(ressym,resdef) and
+         (tabstractnormalvarsym(ressym).inparentfpstruct) then
         begin
         begin
-          if assigned(pd.funcretsym) and
-             tabstractnormalvarsym(pd.funcretsym).inparentfpstruct then
-            begin
-              block:=internalstatements(stat);
-              addstatement(stat,result);
-              load_parentfpstruct_nested_funcret(pd,stat);
-              result:=block;
-            end;
+          block:=internalstatements(stat);
+          addstatement(stat,result);
+          load_parentfpstruct_nested_funcret(ressym,stat);
+          result:=block;
         end;
         end;
     end;
     end;
 
 

+ 23 - 0
compiler/symdef.pas

@@ -828,6 +828,8 @@ interface
           procedure make_external;
           procedure make_external;
           procedure init_genericdecl;
           procedure init_genericdecl;
 
 
+          function getfuncretsyminfo(out ressym: tsym; out resdef: tdef): boolean; virtual;
+
           { returns whether the mangled name or any of its aliases is equal to
           { returns whether the mangled name or any of its aliases is equal to
             s }
             s }
           function  has_alias_name(const s: TSymStr):boolean;
           function  has_alias_name(const s: TSymStr):boolean;
@@ -6046,6 +6048,27 @@ implementation
       end;
       end;
 
 
 
 
+    function tprocdef.getfuncretsyminfo(out ressym: tsym; out resdef: tdef): boolean;
+      begin
+        result:=false;
+        if proctypeoption=potype_constructor then
+          begin
+            result:=true;
+            ressym:=tsym(parast.Find('self'));
+            resdef:=tabstractnormalvarsym(ressym).vardef;
+            { and TP-style constructors return a pointer to self }
+            if is_object(resdef) then
+              resdef:=cpointerdef.getreusable(resdef);
+          end
+        else if not is_void(returndef) then
+          begin
+            result:=true;
+            ressym:=funcretsym;
+            resdef:=tabstractnormalvarsym(ressym).vardef;
+          end;
+      end;
+
+
     function tprocdef.has_alias_name(const s: TSymStr): boolean;
     function tprocdef.has_alias_name(const s: TSymStr): boolean;
       var
       var
         item : TCmdStrListItem;
         item : TCmdStrListItem;

+ 15 - 10
packages/fcl-passrc/src/pasresolver.pp

@@ -5904,6 +5904,7 @@ end;
 procedure TPasResolver.FinishProperty(PropEl: TPasProperty);
 procedure TPasResolver.FinishProperty(PropEl: TPasProperty);
 var
 var
   PropType: TPasType;
   PropType: TPasType;
+  ClassOrRecScope: TPasClassOrRecordScope;
   ClassScope: TPasClassScope;
   ClassScope: TPasClassScope;
   AncestorProp: TPasProperty;
   AncestorProp: TPasProperty;
   IndexExpr: TPasExpr;
   IndexExpr: TPasExpr;
@@ -5914,7 +5915,7 @@ var
   begin
   begin
     if PropType<>nil then exit;
     if PropType<>nil then exit;
     AncEl:=nil;
     AncEl:=nil;
-    if ClassScope.AncestorScope<>nil then
+    if (ClassScope<>nil) and (ClassScope.AncestorScope<>nil) then
       AncEl:=ClassScope.AncestorScope.FindElement(PropEl.Name);
       AncEl:=ClassScope.AncestorScope.FindElement(PropEl.Name);
     if AncEl is TPasProperty then
     if AncEl is TPasProperty then
       begin
       begin
@@ -5943,7 +5944,7 @@ var
       // get inherited type
       // get inherited type
       PropType:=GetPasPropertyType(AncestorProp);
       PropType:=GetPasPropertyType(AncestorProp);
       // update DefaultProperty
       // update DefaultProperty
-      if (ClassScope.DefaultProperty=AncestorProp) then
+      if ClassScope.DefaultProperty=AncestorProp then
         ClassScope.DefaultProperty:=PropEl;
         ClassScope.DefaultProperty:=PropEl;
       end;
       end;
   end;
   end;
@@ -6232,7 +6233,7 @@ var
 
 
 var
 var
   ResultType: TPasType;
   ResultType: TPasType;
-  CurClassType: TPasClassType;
+  MembersType: TPasMembersType;
   AccEl: TPasElement;
   AccEl: TPasElement;
   Proc: TPasProcedure;
   Proc: TPasProcedure;
   Arg: TPasArgument;
   Arg: TPasArgument;
@@ -6253,8 +6254,12 @@ begin
           ['published property','"'+VariableModifierNames[m]+'"'],PropEl);
           ['published property','"'+VariableModifierNames[m]+'"'],PropEl);
 
 
   PropType:=nil;
   PropType:=nil;
-  CurClassType:=PropEl.Parent as TPasClassType;
-  ClassScope:=NoNil(CurClassType.CustomData) as TPasClassScope;
+  MembersType:=PropEl.Parent as TPasMembersType;
+  ClassOrRecScope:=NoNil(MembersType.CustomData) as TPasClassOrRecordScope;
+  if ClassOrRecScope is TPasClassScope then
+    ClassScope:=TPasClassScope(ClassOrRecScope)
+  else
+    ClassScope:=nil;
   AncestorProp:=nil;
   AncestorProp:=nil;
   GetPropType;
   GetPropType;
   IndexVal:=nil;
   IndexVal:=nil;
@@ -6461,10 +6466,10 @@ begin
     if PropEl.IsDefault then
     if PropEl.IsDefault then
       begin
       begin
       // set default array property
       // set default array property
-      if (ClassScope.DefaultProperty<>nil)
-          and (ClassScope.DefaultProperty.Parent=PropEl.Parent) then
+      if (ClassOrRecScope.DefaultProperty<>nil)
+          and (ClassOrRecScope.DefaultProperty.Parent=PropEl.Parent) then
         RaiseMsg(20170216151938,nOnlyOneDefaultPropertyIsAllowed,sOnlyOneDefaultPropertyIsAllowed,[],PropEl);
         RaiseMsg(20170216151938,nOnlyOneDefaultPropertyIsAllowed,sOnlyOneDefaultPropertyIsAllowed,[],PropEl);
-      ClassScope.DefaultProperty:=PropEl;
+      ClassOrRecScope.DefaultProperty:=PropEl;
       end;
       end;
     EmitTypeHints(PropEl,PropEl.VarType);
     EmitTypeHints(PropEl,PropEl.VarType);
   finally
   finally
@@ -14759,7 +14764,7 @@ procedure TPasResolver.CheckFoundElement(
 var
 var
   Proc: TPasProcedure;
   Proc: TPasProcedure;
   Context: TPasElement;
   Context: TPasElement;
-  FoundContext: TPasClassType;
+  FoundContext: TPasMembersType;
   StartScope: TPasScope;
   StartScope: TPasScope;
   OnlyTypeMembers, IsClassOf: Boolean;
   OnlyTypeMembers, IsClassOf: Boolean;
   TypeEl: TPasType;
   TypeEl: TPasType;
@@ -14956,7 +14961,7 @@ begin
   if FindData.Found.Visibility in [visPrivate,visProtected,visStrictPrivate,visStrictProtected] then
   if FindData.Found.Visibility in [visPrivate,visProtected,visStrictPrivate,visStrictProtected] then
     begin
     begin
     Context:=GetVisibilityContext;
     Context:=GetVisibilityContext;
-    FoundContext:=FindData.Found.Parent as TPasClassType;
+    FoundContext:=FindData.Found.Parent as TPasMembersType;
     case FindData.Found.Visibility of
     case FindData.Found.Visibility of
       visPrivate:
       visPrivate:
         // private members can only be accessed in same module
         // private members can only be accessed in same module

+ 15 - 11
packages/fcl-passrc/src/pparser.pp

@@ -5233,13 +5233,14 @@ function TPasParser.ParseProperty(Parent: TPasElement; const AName: String;
   end;
   end;
 
 
 var
 var
-  isArray , ok: Boolean;
+  isArray , ok, IsClass: Boolean;
   ObjKind: TPasObjKind;
   ObjKind: TPasObjKind;
 begin
 begin
   Result:=TPasProperty(CreateElement(TPasProperty,AName,Parent,AVisibility));
   Result:=TPasProperty(CreateElement(TPasProperty,AName,Parent,AVisibility));
   if IsClassField then
   if IsClassField then
     Include(Result.VarModifiers,vmClass);
     Include(Result.VarModifiers,vmClass);
-  if (Parent<>nil) and (Parent.ClassType=TPasClassType) then
+  IsClass:=(Parent<>nil) and (Parent.ClassType=TPasClassType);
+  if IsClass then
     ObjKind:=TPasClassType(Parent).ObjKind
     ObjKind:=TPasClassType(Parent).ObjKind
   else
   else
     ObjKind:=okClass;
     ObjKind:=okClass;
@@ -5272,17 +5273,20 @@ begin
       Result.WriteAccessorName := GetAccessorName(Result,Result.WriteAccessor);
       Result.WriteAccessorName := GetAccessorName(Result,Result.WriteAccessor);
       NextToken;
       NextToken;
       end;
       end;
-    if CurTokenIsIdentifier('READONLY') then
+    if IsClass and (ObjKind=okDispInterface) then
       begin
       begin
-      Result.DispIDReadOnly:=True;
-      NextToken;
-      end;
-    if CurTokenIsIdentifier('DISPID') then
-      begin
-      NextToken;
-      Result.DispIDExpr := DoParseExpression(Result,Nil);
+      if CurTokenIsIdentifier('READONLY') then
+        begin
+        Result.DispIDReadOnly:=True;
+        NextToken;
+        end;
+      if CurTokenIsIdentifier('DISPID') then
+        begin
+        NextToken;
+        Result.DispIDExpr := DoParseExpression(Result,Nil);
+        end;
       end;
       end;
-    if (ObjKind in [okClass]) and CurTokenIsIdentifier('IMPLEMENTS') then
+    if IsClass and (ObjKind=okClass) and CurTokenIsIdentifier('IMPLEMENTS') then
       ParseImplements;
       ParseImplements;
     if CurTokenIsIdentifier('STORED') then
     if CurTokenIsIdentifier('STORED') then
       begin
       begin

+ 3 - 1
packages/fcl-passrc/tests/tcclasstype.pas

@@ -253,6 +253,8 @@ procedure TTestClassType.StartClass(AncestorName: String; InterfaceList: String)
 Var
 Var
   S : String;
   S : String;
 begin
 begin
+  if FStarted then
+    Fail('TTestClassType.StartClass already started');
   FStarted:=True;
   FStarted:=True;
   S:='TMyClass = Class';
   S:='TMyClass = Class';
   if (AncestorName<>'') then
   if (AncestorName<>'') then
@@ -426,7 +428,7 @@ end;
 procedure TTestClassType.SetUp;
 procedure TTestClassType.SetUp;
 begin
 begin
   inherited SetUp;
   inherited SetUp;
-  FDecl:=TstringList.Create;
+  FDecl:=TStringList.Create;
   FClass:=Nil;
   FClass:=Nil;
   FParent:='';
   FParent:='';
   FStarted:=False;
   FStarted:=False;

+ 3 - 4
packages/fcl-passrc/tests/tcresolver.pas

@@ -488,10 +488,11 @@ type
 
 
     // advanced record
     // advanced record
     Procedure TestAdvRecord;
     Procedure TestAdvRecord;
-    Procedure TestAdvRecord_Private; // ToDo
+    Procedure TestAdvRecord_Private;
+    // ToDO: Procedure TestAdvRecord_PropertyWithoutTypeFail;
     // Todo: Procedure TestAdvRecord_ForwardFail
     // Todo: Procedure TestAdvRecord_ForwardFail
     // ToDo: public, private, strict private
     // ToDo: public, private, strict private
-    // ToDo: TestAdvRecordPublsihedFail
+    // ToDo: TestAdvRecordPublishedFail
     // ToDo: TestAdvRecord_VirtualFail
     // ToDo: TestAdvRecord_VirtualFail
     // ToDo: TestAdvRecord_OverrideFail
     // ToDo: TestAdvRecord_OverrideFail
     // ToDo: constructor, destructor
     // ToDo: constructor, destructor
@@ -7840,8 +7841,6 @@ end;
 
 
 procedure TTestResolver.TestAdvRecord_Private;
 procedure TTestResolver.TestAdvRecord_Private;
 begin
 begin
-  exit;
-
   StartProgram(false);
   StartProgram(false);
   Add([
   Add([
   '{$modeswitch advancedrecords}',
   '{$modeswitch advancedrecords}',

+ 153 - 21
packages/fcl-passrc/tests/tctypeparser.pas

@@ -171,16 +171,30 @@ type
 
 
   { TTestRecordTypeParser }
   { TTestRecordTypeParser }
 
 
-  TTestRecordTypeParser= Class(TBaseTestTypeParser)
+  TTestRecordTypeParser = Class(TBaseTestTypeParser)
   private
   private
+    FDecl : TStrings;
+    FAdvanced,
+    FEnded,
+    FStarted: boolean;
+    FRecord: TPasRecordType;
+    FMember1: TPasElement;
     function GetC(AIndex: Integer): TPasConst;
     function GetC(AIndex: Integer): TPasConst;
     Function GetField(AIndex : Integer; R : TPasRecordType) : TPasVariable;
     Function GetField(AIndex : Integer; R : TPasRecordType) : TPasVariable;
     Function GetField(AIndex : Integer; R : TPasVariant) : TPasVariable;
     Function GetField(AIndex : Integer; R : TPasVariant) : TPasVariable;
     function GetF(AIndex: Integer): TPasVariable;
     function GetF(AIndex: Integer): TPasVariable;
-    function GetR: TPasRecordType;
+    function GetM(AIndex : Integer): TPasElement;
     Function GetVariant(AIndex : Integer; R : TPasRecordType) : TPasVariant;
     Function GetVariant(AIndex : Integer; R : TPasRecordType) : TPasVariant;
     function GetV(AIndex: Integer): TPasVariant;
     function GetV(AIndex: Integer): TPasVariant;
   Protected
   Protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+    Procedure StartRecord(Advanced: boolean = false);
+    Procedure EndRecord(AEnd : String = 'end');
+    Procedure AddMember(S : String);
+    Procedure ParseRecord;
+    Procedure ParseRecordFail(Msg: string; MsgNumber: integer);
+    Procedure DoParseRecord;
     Procedure TestFields(Const Fields : Array of string; AHint : String; HaveVariant : Boolean = False);
     Procedure TestFields(Const Fields : Array of string; AHint : String; HaveVariant : Boolean = False);
     procedure AssertVariantSelector(AName, AType: string);
     procedure AssertVariantSelector(AName, AType: string);
     procedure AssertConst1(Hints: TPasMemberHints);
     procedure AssertConst1(Hints: TPasMemberHints);
@@ -216,12 +230,15 @@ type
     procedure DoTestVariantNestedVariantFirstDeprecated(Const AHint : string);
     procedure DoTestVariantNestedVariantFirstDeprecated(Const AHint : string);
     procedure DoTestVariantNestedVariantSecondDeprecated(const AHint: string);
     procedure DoTestVariantNestedVariantSecondDeprecated(const AHint: string);
     procedure DoTestVariantNestedVariantBothDeprecated(const AHint: string);
     procedure DoTestVariantNestedVariantBothDeprecated(const AHint: string);
-    Property TheRecord : TPasRecordType Read GetR;
+    Property TheRecord : TPasRecordType Read FRecord;
+    Property Advanced: boolean read FAdvanced;
     Property Const1 : TPasConst Index 0 Read GetC;
     Property Const1 : TPasConst Index 0 Read GetC;
     Property Field1 : TPasVariable Index 0 Read GetF;
     Property Field1 : TPasVariable Index 0 Read GetF;
     Property Field2 : TPasVariable Index 1 Read GetF;
     Property Field2 : TPasVariable Index 1 Read GetF;
     Property Variant1 : TPasVariant Index 0 Read GetV;
     Property Variant1 : TPasVariant Index 0 Read GetV;
     Property Variant2 : TPasVariant Index 1 Read GetV;
     Property Variant2 : TPasVariant Index 1 Read GetV;
+    Property Members[AIndex : Integer] : TPasElement Read GetM;
+    Property Member1 : TPasElement Read FMember1;
   Published
   Published
     Procedure TestEmpty;
     Procedure TestEmpty;
     Procedure TestEmptyComment;
     Procedure TestEmptyComment;
@@ -333,6 +350,9 @@ type
     Procedure TestVariantNestedVariantBothDeprecatedDeprecated;
     Procedure TestVariantNestedVariantBothDeprecatedDeprecated;
     Procedure TestVariantNestedVariantBothDeprecatedPlatform;
     Procedure TestVariantNestedVariantBothDeprecatedPlatform;
     Procedure TestOperatorField;
     Procedure TestOperatorField;
+    Procedure TestPropertyFail;
+    Procedure TestAdvRec_Property;
+    Procedure TestAdvRec_PropertyImplementsFail;
   end;
   end;
 
 
   { TTestProcedureTypeParser }
   { TTestProcedureTypeParser }
@@ -1148,7 +1168,7 @@ end;
 
 
 function TTestRecordTypeParser.GetC(AIndex: Integer): TPasConst;
 function TTestRecordTypeParser.GetC(AIndex: Integer): TPasConst;
 begin
 begin
-  Result:=TObject(GetR.Members[AIndex]) as TPasConst;
+  Result:=TObject(TheRecord.Members[AIndex]) as TPasConst;
 end;
 end;
 
 
 function TTestRecordTypeParser.GetField(AIndex: Integer; R: TPasRecordType
 function TTestRecordTypeParser.GetField(AIndex: Integer; R: TPasRecordType
@@ -1174,12 +1194,18 @@ end;
 
 
 function TTestRecordTypeParser.GetF(AIndex: Integer): TPasVariable;
 function TTestRecordTypeParser.GetF(AIndex: Integer): TPasVariable;
 begin
 begin
-  Result:=GetField(AIndex,GetR);
+  Result:=GetField(AIndex,TheRecord);
 end;
 end;
 
 
-function TTestRecordTypeParser.GetR: TPasRecordType;
+function TTestRecordTypeParser.GetM(AIndex : Integer): TPasElement;
 begin
 begin
-  Result:=TheType as TPasRecordType;
+  AssertNotNull('Have Record',TheRecord);
+  if (AIndex>=TheRecord.Members.Count) then
+    Fail('No member '+IntToStr(AIndex));
+  AssertNotNull('Have member'+IntToStr(AIndex),TheRecord.Members[AIndex]);
+  If Not (TObject(TheRecord.Members[AIndex]) is TPasElement) then
+    Fail('Member '+IntTostr(AIndex)+' is not a TPasElement');
+  Result:=TPasElement(TheRecord.Members[AIndex])
 end;
 end;
 
 
 function TTestRecordTypeParser.GetVariant(AIndex: Integer; R: TPasRecordType
 function TTestRecordTypeParser.GetVariant(AIndex: Integer; R: TPasRecordType
@@ -1194,7 +1220,94 @@ end;
 
 
 function TTestRecordTypeParser.GetV(AIndex: Integer): TPasVariant;
 function TTestRecordTypeParser.GetV(AIndex: Integer): TPasVariant;
 begin
 begin
-  Result:=GetVariant(AIndex,GetR);
+  Result:=GetVariant(AIndex,TheRecord);
+end;
+
+procedure TTestRecordTypeParser.SetUp;
+begin
+  inherited SetUp;
+  FDecl:=TStringList.Create;
+  FStarted:=false;
+  FEnded:=false;
+end;
+
+procedure TTestRecordTypeParser.TearDown;
+begin
+  FreeAndNil(FDecl);
+  inherited TearDown;
+end;
+
+procedure TTestRecordTypeParser.StartRecord(Advanced: boolean);
+var
+  S: String;
+begin
+  if FStarted then
+    Fail('TTestRecordTypeParser.StartRecord already started');
+  FStarted:=True;
+  S:='TMyRecord = record';
+  if Advanced then
+    S:='{$modeswitch advancedrecords}'+sLineBreak+S;
+  FDecl.Add(S);
+end;
+
+procedure TTestRecordTypeParser.EndRecord(AEnd: String);
+begin
+  if FEnded then exit;
+  if not FStarted then
+    StartRecord;
+  FEnded:=True;
+  if (AEnd<>'') then
+    FDecl.Add('  '+AEnd);
+end;
+
+procedure TTestRecordTypeParser.AddMember(S: String);
+begin
+  if Not FStarted then
+    StartRecord;
+  FDecl.Add('    '+S);
+end;
+
+procedure TTestRecordTypeParser.ParseRecord;
+begin
+  DoParseRecord;
+end;
+
+procedure TTestRecordTypeParser.ParseRecordFail(Msg: string; MsgNumber: integer
+  );
+var
+  ok: Boolean;
+begin
+  ok:=false;
+  try
+    ParseRecord;
+  except
+    on E: EParserError do
+      begin
+      AssertEquals('Expected {'+Msg+'}, but got msg {'+Parser.LastMsg+'}',MsgNumber,Parser.LastMsgNumber);
+      ok:=true;
+      end;
+  end;
+  AssertEquals('Missing parser error {'+Msg+'} ('+IntToStr(MsgNumber)+')',true,ok);
+end;
+
+procedure TTestRecordTypeParser.DoParseRecord;
+begin
+  EndRecord;
+  Add('Type');
+  if AddComment then
+    begin
+    Add('// A comment');
+    Engine.NeedComments:=True;
+    end;
+  Add('  '+TrimRight(FDecl.Text)+';');
+  ParseDeclarations;
+  AssertEquals('One record type definition',1,Declarations.Types.Count);
+  AssertEquals('First declaration is type definition.',TPasRecordType,TObject(Declarations.Types[0]).ClassType);
+  FRecord:=TObject(Declarations.Types[0]) as TPasRecordType;
+  TheType:=FRecord; // needed by AssertComment
+  Definition:=TheType; // needed by CheckHint
+  if TheRecord.Members.Count>0 then
+    FMember1:=TObject(TheRecord.Members[0]) as TPasElement;
 end;
 end;
 
 
 procedure TTestRecordTypeParser.TestFields(const Fields: array of string;
 procedure TTestRecordTypeParser.TestFields(const Fields: array of string;
@@ -1205,17 +1318,14 @@ Var
   I : integer;
   I : integer;
 
 
 begin
 begin
-  S:='';
+  StartRecord;
   For I:=Low(Fields) to High(Fields) do
   For I:=Low(Fields) to High(Fields) do
-    begin
-    if (S<>'') then
-      S:=S+sLineBreak;
-    S:=S+'    '+Fields[i];
-    end;
-  if (S<>'') then
-    S:=S+sLineBreak;
-  S:='record'+sLineBreak+s+'  end';
-  ParseType(S,TPasRecordType,AHint);
+    AddMember(Fields[i]);
+  S:='end';
+  if AHint<>'' then
+    S:=S+' '+AHint;
+  EndRecord(S);
+  ParseRecord;
   if HaveVariant then
   if HaveVariant then
     begin
     begin
     AssertNotNull('Have variants',TheRecord.Variants);
     AssertNotNull('Have variants',TheRecord.Variants);
@@ -1228,6 +1338,8 @@ begin
     end;
     end;
   if AddComment then
   if AddComment then
     AssertComment;
     AssertComment;
+  if (AHint<>'') then
+    CheckHint(TPasMemberHint(GetEnumValue(TypeInfo(TPasMemberHint),'h'+AHint)));
 end;
 end;
 
 
 procedure TTestRecordTypeParser.AssertVariantSelector(AName,AType : string);
 procedure TTestRecordTypeParser.AssertVariantSelector(AName,AType : string);
@@ -2411,6 +2523,26 @@ begin
   AssertEquals('Field 1 name','operator',Field1.Name);
   AssertEquals('Field 1 name','operator',Field1.Name);
 end;
 end;
 
 
+procedure TTestRecordTypeParser.TestPropertyFail;
+begin
+  AddMember('Property Something');
+  ParseRecordFail(SErrRecordPropertiesNotAllowed,nErrRecordPropertiesNotAllowed);
+end;
+
+procedure TTestRecordTypeParser.TestAdvRec_Property;
+begin
+  StartRecord(true);
+  AddMember('Property Something: word');
+  ParseRecord;
+end;
+
+procedure TTestRecordTypeParser.TestAdvRec_PropertyImplementsFail;
+begin
+  StartRecord(true);
+  AddMember('Property Something: word implements ISome;');
+  ParseRecordFail('Expected ";"',nParserExpectTokenError);
+end;
+
 { TBaseTestTypeParser }
 { TBaseTestTypeParser }
 
 
 Function TBaseTestTypeParser.ParseType(ASource: String; ATypeClass: TClass;
 Function TBaseTestTypeParser.ParseType(ASource: String; ATypeClass: TClass;
@@ -2437,9 +2569,9 @@ begin
     AssertEquals('One type definition',1,Declarations.Classes.Count)
     AssertEquals('One type definition',1,Declarations.Classes.Count)
   else
   else
     AssertEquals('One type definition',1,Declarations.Types.Count);
     AssertEquals('One type definition',1,Declarations.Types.Count);
-  If (AtypeClass<>Nil) then
+  If ATypeClass<>Nil then
     begin
     begin
-    if ATypeClass.InHeritsFrom(TPasClassType) then
+    if ATypeClass.InheritsFrom(TPasClassType) then
       Result:=TPasType(Declarations.Classes[0])
       Result:=TPasType(Declarations.Classes[0])
     else
     else
       Result:=TPasType(Declarations.Types[0]);
       Result:=TPasType(Declarations.Types[0]);
@@ -2449,7 +2581,7 @@ begin
   FType:=Result;
   FType:=Result;
   Definition:=Result;
   Definition:=Result;
   if (Hint<>'') then
   if (Hint<>'') then
-    CheckHint(TPasMemberHint(Getenumvalue(typeinfo(TPasMemberHint),'h'+Hint)));
+    CheckHint(TPasMemberHint(GetEnumValue(TypeInfo(TPasMemberHint),'h'+Hint)));
 end;
 end;
 
 
 Procedure TBaseTestTypeParser.AssertParseTypeError(ASource: String);
 Procedure TBaseTestTypeParser.AssertParseTypeError(ASource: String);

+ 16 - 0
tests/webtbs/tw33666.pp

@@ -0,0 +1,16 @@
+program Project1;
+
+{$mode objfpc}{$H+}
+{$Codepage UTF8}
+
+type
+  CP437String = type ansistring(437);
+
+var
+  s_cp437_1: CP437String;
+begin
+  s_cp437_1 := '║'; //<--- buggy
+  if (length(s_cp437_1)<> 1) or
+     (ord(s_cp437_1[1])<> 186) then
+    halt(1);
+end.