https://gcc.gnu.org/g:74cdc0d576479756c7faa88f74b041cd9ff51636
commit r15-4897-g74cdc0d576479756c7faa88f74b041cd9ff51636 Author: Javier Miranda <mira...@adacore.com> Date: Tue Oct 8 18:33:37 2024 +0000 ada: Missing precondition runtime check in inherited primitive When a derived tagged type implements interface types in addition to deriving from its parent type, and a primitive inherited from its parent type corresponds to an inherited primitive that has class-wide preconditions, then the generated code fails to check the class-wide preconditions inherited from the interface primitive. gcc/ada/ChangeLog: * einfo.ads (Is_Dispatch_Table_Wrapper): Complete documentation. * exp_ch6.adb (Install_Class_Preconditions_Check): Dispatch table wrappers do not require installing the check since it is performed by the caller. (Class_Preconditions_Subprogram): Use new predicate Is_LSP_Wrapper. * freeze.adb (Check_Inherited_Conditions): Rename Postcond_Wrappers to Condition_Wrappers to handle implicitly inherited subprograms that implement pre-/postconditions inherited from interface primitives. Use new predicate Is_LSP_Wrapper. * sem_disp.adb (Check_Dispatching_Operation): Complete assertion to handle functions returning class-wide types. * exp_util.ads (Is_LSP_Wrapper): New subprogram. * exp_util.adb (Is_LSP_Wrapper): New subprogram. * contracts.adb (Process_Spec_Postconditions): Use Is_LSP_Wrapper. (Process_Inherited_Conditions): Use Is_LSP_Wrapper. * sem_ch6.adb (New_Overloaded_Entity): Use Is_LSP_Wrapper. * sem_util.adb (Nearest_Class_Condition_Subprogram): Use Is_LSP_Wrapper. Diff: --- gcc/ada/contracts.adb | 8 ++--- gcc/ada/einfo.ads | 9 +++-- gcc/ada/exp_ch6.adb | 33 +++++++++++++++++-- gcc/ada/exp_util.adb | 10 ++++++ gcc/ada/exp_util.ads | 5 +++ gcc/ada/freeze.adb | 91 ++++++++++++++++++++++++++++++--------------------- gcc/ada/sem_ch6.adb | 4 +-- gcc/ada/sem_disp.adb | 7 ++++ gcc/ada/sem_util.adb | 4 +-- 9 files changed, 115 insertions(+), 56 deletions(-) diff --git a/gcc/ada/contracts.adb b/gcc/ada/contracts.adb index a93bf622aa13..7e66a54b6752 100644 --- a/gcc/ada/contracts.adb +++ b/gcc/ada/contracts.adb @@ -2934,9 +2934,7 @@ package body Contracts is -- Wrappers of class-wide pre/postconditions reference the -- parent primitive that has the inherited contract. - if Is_Wrapper (Subp_Id) - and then Present (LSP_Subprogram (Subp_Id)) - then + if Is_LSP_Wrapper (Subp_Id) then Subp_Id := LSP_Subprogram (Subp_Id); end if; @@ -4602,9 +4600,7 @@ package body Contracts is -- parent primitive that has the inherited contract and help -- us to climb fast. - if Is_Wrapper (Subp_Id) - and then Present (LSP_Subprogram (Subp_Id)) - then + if Is_LSP_Wrapper (Subp_Id) then Subp_Id := LSP_Subprogram (Subp_Id); end if; diff --git a/gcc/ada/einfo.ads b/gcc/ada/einfo.ads index 2fb45703a4fb..2aae60afae5c 100644 --- a/gcc/ada/einfo.ads +++ b/gcc/ada/einfo.ads @@ -2576,9 +2576,12 @@ package Einfo is -- entity is associated with a dispatch table. -- Is_Dispatch_Table_Wrapper --- Applies to all entities. Set on wrappers built when the subprogram has --- class-wide preconditions or class-wide postconditions affected by --- overriding (AI12-0195). +-- Applies to all entities. Set on wrappers built when a subprogram has +-- class-wide preconditions or postconditions affected by overriding +-- (AI12-0195). Also set on wrappers built when an inherited subprogram +-- implements an interface primitive that has class-wide preconditions +-- or postconditions. In the former case, the entity also has its +-- LSP_Subprogram attribute set. -- Is_Dispatching_Operation -- Defined in all entities. Set for procedures, functions, generic diff --git a/gcc/ada/exp_ch6.adb b/gcc/ada/exp_ch6.adb index c550b1c8c1f0..384324492292 100644 --- a/gcc/ada/exp_ch6.adb +++ b/gcc/ada/exp_ch6.adb @@ -7748,9 +7748,7 @@ package body Exp_Ch6 is -- Wrappers of class-wide pre/postconditions reference the -- parent primitive that has the inherited contract. - if Is_Wrapper (Subp_Id) - and then Present (LSP_Subprogram (Subp_Id)) - then + if Is_LSP_Wrapper (Subp_Id) then Subp_Id := LSP_Subprogram (Subp_Id); end if; @@ -7796,6 +7794,35 @@ package body Exp_Ch6 is elsif Is_Thunk (Current_Scope) then return; + + -- The call to the inherited primitive in a dispatch table wrapper must + -- not have the class-wide precondition check since it is installed in + -- the caller of the wrapper. This is also required to avoid the wrong + -- evaluation of class-wide preconditions in Condition_Wrappers (ie. + -- wrappers of inherited primitives that implement additional interface + -- primitives that have preconditions). + + -- For example: + -- type Typ is tagged null record; + -- procedure Prim (X : T) with Pre'Class => False; + + -- type Iface is interface; + -- procedure Prim (X : Iface) is abstract with Pre'Class => True; + + -- type DT is new Typ and Iface with null record; + -- <internally built dispatch table wrapper of inherited Prim> + + -- The class-wide preconditions of the wrapper must not fail due to the + -- disjunction of the class-wide preconditions of subprograms Typ.Prim + -- and Iface.Prim. If the precondition check were placed in the + -- wrapper's call to the inherited parent primitive, its class-wide + -- condition would incorrectly be reported as failed at runtime. + + elsif Is_Dispatch_Table_Wrapper (Current_Scope) + or else (Chars (Current_Scope) = Name_uWrapped_Statements + and then Is_Dispatch_Table_Wrapper (Scope (Current_Scope))) + then + return; end if; Subp := Entity (Name (Call_Node)); diff --git a/gcc/ada/exp_util.adb b/gcc/ada/exp_util.adb index 400d5d86fbaf..4029ea6263c9 100644 --- a/gcc/ada/exp_util.adb +++ b/gcc/ada/exp_util.adb @@ -9193,6 +9193,16 @@ package body Exp_Util is return Is_Tagged_Type (Typ) and then Is_Library_Level_Entity (Typ); end Is_Library_Level_Tagged_Type; + -------------------- + -- Is_LSP_Wrapper -- + -------------------- + + function Is_LSP_Wrapper (E : Entity_Id) return Boolean is + begin + return Is_Dispatch_Table_Wrapper (E) + and then Present (LSP_Subprogram (E)); + end Is_LSP_Wrapper; + -------------------------- -- Is_Non_BIP_Func_Call -- -------------------------- diff --git a/gcc/ada/exp_util.ads b/gcc/ada/exp_util.ads index 49e75c79d35a..898d712f0498 100644 --- a/gcc/ada/exp_util.ads +++ b/gcc/ada/exp_util.ads @@ -789,6 +789,11 @@ package Exp_Util is -- Return True if Typ is a library level tagged type. Currently we use -- this information to build statically allocated dispatch tables. + function Is_LSP_Wrapper (E : Entity_Id) return Boolean; + -- Return True if E is a wrapper built when a subprogram has class-wide + -- preconditions or postconditions affected by overriding (AI12-0195). + -- LSP stands for Liskov Substitution Principle. + function Is_Non_BIP_Func_Call (Expr : Node_Id) return Boolean; -- Determine whether node Expr denotes a non build-in-place function call diff --git a/gcc/ada/freeze.adb b/gcc/ada/freeze.adb index 9c14e1f1a700..c7e3be028a7c 100644 --- a/gcc/ada/freeze.adb +++ b/gcc/ada/freeze.adb @@ -1463,7 +1463,7 @@ package body Freeze is Par_Prim : Entity_Id; Prim : Entity_Id; - type Wrapper_Kind is (No_Wrapper, LSP_Wrapper, Postcond_Wrapper); + type Wrapper_Kind is (No_Wrapper, LSP_Wrapper, Condition_Wrapper); Wrapper_Needed : Wrapper_Kind; -- Kind of wrapper needed by a given inherited primitive of tagged @@ -1471,8 +1471,9 @@ package body Freeze is -- * No_Wrapper: No wrapper is needed. -- * LSP_Wrapper: Wrapper that handles inherited class-wide pre/post -- conditions that call overridden primitives. - -- * Postcond_Wrapper: Wrapper that handles postconditions of interface - -- primitives. + -- * Condition_Wrapper: Wrapper of inherited subprogram that implements + -- additional interface primitives of the derived type that have + -- class-wide pre-/postconditions. function Build_DTW_Body (Loc : Source_Ptr; @@ -1855,9 +1856,9 @@ package body Freeze is -- List containing identifiers of built wrappers. Used to defer building -- and analyzing their class-wide precondition subprograms. - Postcond_Candidates_List : Elist_Id := No_Elist; + Condition_Candidates_List : Elist_Id := No_Elist; -- List containing inherited primitives of tagged type R that implement - -- interface primitives that have postconditions. + -- interface primitives that have pre-/postconditions. -- Start of processing for Check_Inherited_Conditions @@ -1907,9 +1908,7 @@ package body Freeze is -- When the primitive is an LSP wrapper we climb to the parent -- primitive that has the inherited contract. - if Is_Wrapper (Par_Prim) - and then Present (LSP_Subprogram (Par_Prim)) - then + if Is_LSP_Wrapper (Par_Prim) then Par_Prim := LSP_Subprogram (Par_Prim); end if; @@ -1943,7 +1942,7 @@ package body Freeze is end loop; -- Collect inherited primitives that may need a wrapper to handle - -- postconditions of interface primitives; done to improve the + -- pre-/postconditions of interface primitives; done to improve the -- performance when checking if postcondition wrappers are needed. Op_Node := First_Elmt (Prim_Ops); @@ -1952,13 +1951,16 @@ package body Freeze is if Present (Interface_Alias (Prim)) and then not Comes_From_Source (Alias (Prim)) - and then Present (Class_Postconditions (Interface_Alias (Prim))) + and then + (Present (Class_Preconditions (Interface_Alias (Prim))) + or else + Present (Class_Postconditions (Interface_Alias (Prim)))) then - if No (Postcond_Candidates_List) then - Postcond_Candidates_List := New_Elmt_List; + if No (Condition_Candidates_List) then + Condition_Candidates_List := New_Elmt_List; end if; - Append_Unique_Elmt (Alias (Prim), Postcond_Candidates_List); + Append_Unique_Elmt (Alias (Prim), Condition_Candidates_List); end if; Next_Elmt (Op_Node); @@ -1986,9 +1988,7 @@ package body Freeze is -- When the primitive is an LSP wrapper we climb to the parent -- primitive that has the inherited contract. - if Is_Wrapper (Par_Prim) - and then Present (LSP_Subprogram (Par_Prim)) - then + if Is_LSP_Wrapper (Par_Prim) then Par_Prim := LSP_Subprogram (Par_Prim); end if; @@ -2014,12 +2014,12 @@ package body Freeze is -- implements additional interface types, and this inherited -- primitive covers an interface primitive of these additional -- interface types that has class-wide postconditions, then it - -- requires a postconditions wrapper. + -- requires a pre-/postconditions wrapper. if Wrapper_Needed = No_Wrapper and then Present (Interfaces (R)) - and then Present (Postcond_Candidates_List) - and then Contains (Postcond_Candidates_List, Prim) + and then Present (Condition_Candidates_List) + and then Contains (Condition_Candidates_List, Prim) then declare Elmt : Elmt_Id; @@ -2029,7 +2029,8 @@ package body Freeze is begin Elmt := First_Elmt (Prim_Ops); - while Present (Elmt) loop + + Search : while Present (Elmt) loop Ent := Node (Elmt); -- Perform the search relying on the internal entities @@ -2039,7 +2040,9 @@ package body Freeze is if Present (Interface_Alias (Ent)) and then (Alias (Ent)) = Prim and then - Present (Class_Postconditions (Interface_Alias (Ent))) + (Present (Class_Preconditions (Interface_Alias (Ent))) + or else Present (Class_Postconditions + (Interface_Alias (Ent)))) then Iface := Find_Dispatching_Type (Interface_Alias (Ent)); @@ -2052,8 +2055,8 @@ package body Freeze is Iface_Elmt := First_Elmt (Interfaces (R)); while Present (Iface_Elmt) loop if Node (Iface_Elmt) = Iface then - Wrapper_Needed := Postcond_Wrapper; - exit; + Wrapper_Needed := Condition_Wrapper; + exit Search; end if; Next_Elmt (Iface_Elmt); @@ -2061,7 +2064,7 @@ package body Freeze is end if; Next_Elmt (Elmt); - end loop; + end loop Search; end; end if; end if; @@ -2108,7 +2111,8 @@ package body Freeze is -- LSP wrappers reference the parent primitive that has the -- the class-wide pre/post condition that calls overridden - -- primitives. + -- primitives. Condition wrappers do not have this attribute + -- (see predicate Is_LSP_Wrapper). if Wrapper_Needed = LSP_Wrapper then Set_LSP_Subprogram (DTW_Id, Par_Prim); @@ -2124,11 +2128,12 @@ package body Freeze is Set_Sloc (DTW_Id, Sloc (Prim)); - -- For inherited class-wide preconditions the DTW wrapper - -- reuses the ICW of the parent (which checks the parent - -- interpretation of the class-wide preconditions); the - -- interpretation of the class-wide preconditions for the - -- inherited subprogram is checked at the caller side. + -- For LSP_Wrappers of subprograms that inherit class-wide + -- preconditions the DTW wrapper reuses the ICW of the parent + -- (which checks the parent interpretation of the class-wide + -- preconditions); the interpretation of the class-wide + -- preconditions for the inherited subprogram is checked + -- at the caller side. -- When the subprogram inherits class-wide postconditions -- the DTW also checks the interpretation of the class-wide @@ -2137,12 +2142,14 @@ package body Freeze is -- the class-wide postconditions. -- procedure Prim (F1 : T1; ...) is - -- [ pragma Check (Postcondition, Expr); ] + -- [ pragma Postcondition (check => Expr); ] -- begin -- Par_Prim_ICW (Par_Type (F1), ...); -- end; - if Present (Indirect_Call_Wrapper (Par_Prim)) then + if Wrapper_Needed = LSP_Wrapper + and then Present (Indirect_Call_Wrapper (Par_Prim)) + then DTW_Body := Build_DTW_Body (Loc, DTW_Spec => DTW_Spec, @@ -2150,19 +2157,27 @@ package body Freeze is Par_Prim => Par_Prim, Wrapped_Subp => Indirect_Call_Wrapper (Par_Prim)); - -- For subprograms that only inherit class-wide postconditions - -- the DTW wrapper calls the parent primitive (which on its - -- body checks the interpretation of the class-wide post- - -- conditions for the parent subprogram), and the DTW checks - -- the interpretation of the class-wide postconditions for the + -- For LSP_Wrappers of subprograms that only inherit class-wide + -- postconditions, and also for Condition_Wrappers (wrappers of + -- inherited subprograms that implement additional interface + -- primitives that have class-wide pre-/postconditions), the + -- DTW wrapper calls the parent primitive (which on its body + -- checks the interpretation of the class-wide post-conditions + -- for the parent subprogram), and the DTW checks the + -- interpretation of the class-wide postconditions for the -- inherited subprogram. -- procedure Prim (F1 : T1; ...) is - -- pragma Check (Postcondition, Expr); + -- pragma Postcondition (check => Expr); -- begin -- Par_Prim (Par_Type (F1), ...); -- end; + -- No class-wide preconditions runtime check is generated for + -- this wrapper call to the parent primitive, since the check + -- is performed by the caller of the DTW wrapper (see routine + -- Install_Class_Preconditions_Check). + else DTW_Body := Build_DTW_Body (Loc, diff --git a/gcc/ada/sem_ch6.adb b/gcc/ada/sem_ch6.adb index 8cf191d751bd..944f5ca365ad 100644 --- a/gcc/ada/sem_ch6.adb +++ b/gcc/ada/sem_ch6.adb @@ -12651,9 +12651,7 @@ package body Sem_Ch6 is -- chain of ancestor primitives (see Map_Primitives). They -- don't inherit contracts. - if Is_Wrapper (S) - and then Present (LSP_Subprogram (S)) - then + if Is_LSP_Wrapper (S) then Set_Overridden_Operation (S, Ultimate_Alias (E)); -- For entities generated by Derive_Subprograms the diff --git a/gcc/ada/sem_disp.adb b/gcc/ada/sem_disp.adb index 203e9141624c..971192ca64a6 100644 --- a/gcc/ada/sem_disp.adb +++ b/gcc/ada/sem_disp.adb @@ -1458,8 +1458,15 @@ package body Sem_Disp is pragma Assert ((Ekind (Subp) = E_Function and then Is_Dispatching_Operation (Old_Subp) + and then not Is_Class_Wide_Type (Etype (Subp)) and then Is_Null_Extension (Base_Type (Etype (Subp)))) + or else + (Ekind (Subp) = E_Function + and then Is_Dispatching_Operation (Old_Subp) + and then Is_Class_Wide_Type (Etype (Subp)) + and then Is_Null_Extension (Root_Type (Etype (Subp)))) + or else (Ekind (Subp) = E_Procedure and then Is_Dispatching_Operation (Old_Subp) diff --git a/gcc/ada/sem_util.adb b/gcc/ada/sem_util.adb index 5c32b0ba9b29..5d3a4e68c841 100644 --- a/gcc/ada/sem_util.adb +++ b/gcc/ada/sem_util.adb @@ -22246,9 +22246,7 @@ package body Sem_Util is -- Wrappers of class-wide pre/postconditions reference the -- parent primitive that has the inherited contract. - if Is_Wrapper (Subp_Id) - and then Present (LSP_Subprogram (Subp_Id)) - then + if Is_LSP_Wrapper (Subp_Id) then Subp_Id := LSP_Subprogram (Subp_Id); end if;