diff options
author | Javier Miranda <miranda@adacore.com> | 2021-09-04 13:11:34 -0400 |
---|---|---|
committer | Pierre-Marie de Rodat <derodat@adacore.com> | 2021-10-25 15:07:19 +0000 |
commit | 67397bb9888e72fe300746ee9c77b83ce367b733 (patch) | |
tree | ddcc5b047f41927b49cbf97520203812f02d6827 /gcc | |
parent | 19e7eae5b917d782d20d59f3cbe3c344a06aafb7 (diff) | |
download | gcc-67397bb9888e72fe300746ee9c77b83ce367b733.zip gcc-67397bb9888e72fe300746ee9c77b83ce367b733.tar.gz gcc-67397bb9888e72fe300746ee9c77b83ce367b733.tar.bz2 |
[Ada] Ada 2022: Class-wide types and formal abstract subprograms
gcc/ada/
* sem_ch8.adb (Build_Class_Wide_Wrapper): Previous version split
in two subprograms to factorize its functionality:
Find_Suitable_Candidate, and Build_Class_Wide_Wrapper. These
routines are also placed in the new subprogram
Handle_Instance_With_Class_Wide_Type.
(Handle_Instance_With_Class_Wide_Type): New subprogram that
encapsulates all the code that handles instantiations with
class-wide types.
(Analyze_Subprogram_Renaming): Adjust code to invoke the new
nested subprogram Handle_Instance_With_Class_Wide_Type; adjust
documentation.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ada/sem_ch8.adb | 1377 |
1 files changed, 784 insertions, 593 deletions
diff --git a/gcc/ada/sem_ch8.adb b/gcc/ada/sem_ch8.adb index a7b3a16..1513cd5 100644 --- a/gcc/ada/sem_ch8.adb +++ b/gcc/ada/sem_ch8.adb @@ -2109,42 +2109,6 @@ package body Sem_Ch8 is Old_S : Entity_Id := Empty; Rename_Spec : Entity_Id; - procedure Build_Class_Wide_Wrapper - (Ren_Id : out Entity_Id; - Wrap_Id : out Entity_Id); - -- Ada 2012 (AI05-0071): A generic/instance scenario involving a formal - -- type with unknown discriminants and a generic primitive operation of - -- the said type with a box require special processing when the actual - -- is a class-wide type: - -- - -- generic - -- type Formal_Typ (<>) is private; - -- with procedure Prim_Op (Param : Formal_Typ) is <>; - -- package Gen is ... - -- - -- package Inst is new Gen (Actual_Typ'Class); - -- - -- In this case the general renaming mechanism used in the prologue of - -- an instance no longer applies: - -- - -- procedure Prim_Op (Param : Formal_Typ) renames Prim_Op; - -- - -- The above is replaced the following wrapper/renaming combination: - -- - -- procedure Wrapper (Param : Formal_Typ) is -- wrapper - -- begin - -- Prim_Op (Param); -- primitive - -- end Wrapper; - -- - -- procedure Prim_Op (Param : Formal_Typ) renames Wrapper; - -- - -- This transformation applies only if there is no explicit visible - -- class-wide operation at the point of the instantiation. Ren_Id is - -- the entity of the renaming declaration. When the transformation - -- applies, Wrap_Id is the entity of the generated class-wide wrapper - -- (or Any_Id). Otherwise, Wrap_Id is the entity of the class-wide - -- operation. - procedure Check_Null_Exclusion (Ren : Entity_Id; Sub : Entity_Id); @@ -2170,9 +2134,21 @@ package body Sem_Ch8 is -- incomplete untagged formal (RM 13.14(10.2/3)). function Has_Class_Wide_Actual return Boolean; - -- Ada 2012 (AI05-071, AI05-0131): True if N is the renaming for a - -- defaulted formal subprogram where the actual for the controlling - -- formal type is class-wide. + -- Ada 2012 (AI05-071, AI05-0131) and Ada 2022 (AI12-0165): True if N is + -- the renaming for a defaulted formal subprogram where the actual for + -- the controlling formal type is class-wide. + + procedure Handle_Instance_With_Class_Wide_Type + (Inst_Node : Node_Id; + Ren_Id : Entity_Id; + Wrapped_Prim : out Entity_Id; + Wrap_Id : out Entity_Id); + -- Ada 2012 (AI05-0071), Ada 2022 (AI12-0165): when the actual type + -- of an instantiation is a class-wide type T'Class we may need to + -- wrap a primitive operation of T; this routine looks for a suitable + -- primitive to be wrapped and (if the wrapper is required) returns the + -- Id of the wrapped primitive and the Id of the built wrapper. Ren_Id + -- is the defining entity for the renamed subprogram specification. function Original_Subprogram (Subp : Entity_Id) return Entity_Id; -- Find renamed entity when the declaration is a renaming_as_body and @@ -2181,550 +2157,6 @@ package body Sem_Ch8 is -- before the subprogram it completes is frozen, and renaming indirectly -- renames the subprogram itself.(Defect Report 8652/0027). - ------------------------------ - -- Build_Class_Wide_Wrapper -- - ------------------------------ - - procedure Build_Class_Wide_Wrapper - (Ren_Id : out Entity_Id; - Wrap_Id : out Entity_Id) - is - Loc : constant Source_Ptr := Sloc (N); - - function Build_Call - (Subp_Id : Entity_Id; - Params : List_Id) return Node_Id; - -- Create a dispatching call to invoke routine Subp_Id with actuals - -- built from the parameter specifications of list Params. - - function Build_Expr_Fun_Call - (Subp_Id : Entity_Id; - Params : List_Id) return Node_Id; - -- Create a dispatching call to invoke function Subp_Id with actuals - -- built from the parameter specifications of list Params. Return - -- directly the call, so that it can be used inside an expression - -- function. This is a specificity of the GNATprove mode. - - function Build_Spec (Subp_Id : Entity_Id) return Node_Id; - -- Create a subprogram specification based on the subprogram profile - -- of Subp_Id. - - function Find_Primitive (Typ : Entity_Id) return Entity_Id; - -- Find a primitive subprogram of type Typ which matches the profile - -- of the renaming declaration. - - procedure Interpretation_Error (Subp_Id : Entity_Id); - -- Emit a continuation error message suggesting subprogram Subp_Id as - -- a possible interpretation. - - function Is_Intrinsic_Equality (Subp_Id : Entity_Id) return Boolean; - -- Determine whether subprogram Subp_Id denotes the intrinsic "=" - -- operator. - - function Is_Suitable_Candidate (Subp_Id : Entity_Id) return Boolean; - -- Determine whether subprogram Subp_Id is a suitable candidate for - -- the role of a wrapped subprogram. - - ---------------- - -- Build_Call -- - ---------------- - - function Build_Call - (Subp_Id : Entity_Id; - Params : List_Id) return Node_Id - is - Actuals : constant List_Id := New_List; - Call_Ref : constant Node_Id := New_Occurrence_Of (Subp_Id, Loc); - Formal : Node_Id; - - begin - -- Build the actual parameters of the call - - Formal := First (Params); - while Present (Formal) loop - Append_To (Actuals, - Make_Identifier (Loc, Chars (Defining_Identifier (Formal)))); - Next (Formal); - end loop; - - -- Generate: - -- return Subp_Id (Actuals); - - if Ekind (Subp_Id) in E_Function | E_Operator then - return - Make_Simple_Return_Statement (Loc, - Expression => - Make_Function_Call (Loc, - Name => Call_Ref, - Parameter_Associations => Actuals)); - - -- Generate: - -- Subp_Id (Actuals); - - else - return - Make_Procedure_Call_Statement (Loc, - Name => Call_Ref, - Parameter_Associations => Actuals); - end if; - end Build_Call; - - ------------------------- - -- Build_Expr_Fun_Call -- - ------------------------- - - function Build_Expr_Fun_Call - (Subp_Id : Entity_Id; - Params : List_Id) return Node_Id - is - Actuals : constant List_Id := New_List; - Call_Ref : constant Node_Id := New_Occurrence_Of (Subp_Id, Loc); - Formal : Node_Id; - - begin - pragma Assert (Ekind (Subp_Id) in E_Function | E_Operator); - - -- Build the actual parameters of the call - - Formal := First (Params); - while Present (Formal) loop - Append_To (Actuals, - Make_Identifier (Loc, Chars (Defining_Identifier (Formal)))); - Next (Formal); - end loop; - - -- Generate: - -- Subp_Id (Actuals); - - return - Make_Function_Call (Loc, - Name => Call_Ref, - Parameter_Associations => Actuals); - end Build_Expr_Fun_Call; - - ---------------- - -- Build_Spec -- - ---------------- - - function Build_Spec (Subp_Id : Entity_Id) return Node_Id is - Params : constant List_Id := Copy_Parameter_List (Subp_Id); - Spec_Id : constant Entity_Id := - Make_Defining_Identifier (Loc, - Chars => New_External_Name (Chars (Subp_Id), 'R')); - - begin - if Ekind (Formal_Spec) = E_Procedure then - return - Make_Procedure_Specification (Loc, - Defining_Unit_Name => Spec_Id, - Parameter_Specifications => Params); - else - return - Make_Function_Specification (Loc, - Defining_Unit_Name => Spec_Id, - Parameter_Specifications => Params, - Result_Definition => - New_Copy_Tree (Result_Definition (Spec))); - end if; - end Build_Spec; - - -------------------- - -- Find_Primitive -- - -------------------- - - function Find_Primitive (Typ : Entity_Id) return Entity_Id is - procedure Replace_Parameter_Types (Spec : Node_Id); - -- Given a specification Spec, replace all class-wide parameter - -- types with reference to type Typ. - - ----------------------------- - -- Replace_Parameter_Types -- - ----------------------------- - - procedure Replace_Parameter_Types (Spec : Node_Id) is - Formal : Node_Id; - Formal_Id : Entity_Id; - Formal_Typ : Node_Id; - - begin - Formal := First (Parameter_Specifications (Spec)); - while Present (Formal) loop - Formal_Id := Defining_Identifier (Formal); - Formal_Typ := Parameter_Type (Formal); - - -- Create a new entity for each class-wide formal to prevent - -- aliasing with the original renaming. Replace the type of - -- such a parameter with the candidate type. - - if Nkind (Formal_Typ) = N_Identifier - and then Is_Class_Wide_Type (Etype (Formal_Typ)) - then - Set_Defining_Identifier (Formal, - Make_Defining_Identifier (Loc, Chars (Formal_Id))); - - Set_Parameter_Type (Formal, New_Occurrence_Of (Typ, Loc)); - end if; - - Next (Formal); - end loop; - end Replace_Parameter_Types; - - -- Local variables - - Alt_Ren : constant Node_Id := New_Copy_Tree (N); - Alt_Nam : constant Node_Id := Name (Alt_Ren); - Alt_Spec : constant Node_Id := Specification (Alt_Ren); - Subp_Id : Entity_Id; - - -- Start of processing for Find_Primitive - - begin - -- Each attempt to find a suitable primitive of a particular type - -- operates on its own copy of the original renaming. As a result - -- the original renaming is kept decoration and side-effect free. - - -- Inherit the overloaded status of the renamed subprogram name - - if Is_Overloaded (Nam) then - Set_Is_Overloaded (Alt_Nam); - Save_Interps (Nam, Alt_Nam); - end if; - - -- The copied renaming is hidden from visibility to prevent the - -- pollution of the enclosing context. - - Set_Defining_Unit_Name (Alt_Spec, Make_Temporary (Loc, 'R')); - - -- The types of all class-wide parameters must be changed to the - -- candidate type. - - Replace_Parameter_Types (Alt_Spec); - - -- Try to find a suitable primitive which matches the altered - -- profile of the renaming specification. - - Subp_Id := - Find_Renamed_Entity - (N => Alt_Ren, - Nam => Name (Alt_Ren), - New_S => Analyze_Subprogram_Specification (Alt_Spec), - Is_Actual => Is_Actual); - - -- Do not return Any_Id if the resolion of the altered profile - -- failed as this complicates further checks on the caller side, - -- return Empty instead. - - if Subp_Id = Any_Id then - return Empty; - else - return Subp_Id; - end if; - end Find_Primitive; - - -------------------------- - -- Interpretation_Error -- - -------------------------- - - procedure Interpretation_Error (Subp_Id : Entity_Id) is - begin - Error_Msg_Sloc := Sloc (Subp_Id); - - if Is_Internal (Subp_Id) then - Error_Msg_NE - ("\\possible interpretation: predefined & #", - Spec, Formal_Spec); - else - Error_Msg_NE - ("\\possible interpretation: & defined #", Spec, Formal_Spec); - end if; - end Interpretation_Error; - - --------------------------- - -- Is_Intrinsic_Equality -- - --------------------------- - - function Is_Intrinsic_Equality (Subp_Id : Entity_Id) return Boolean is - begin - return - Ekind (Subp_Id) = E_Operator - and then Chars (Subp_Id) = Name_Op_Eq - and then Is_Intrinsic_Subprogram (Subp_Id); - end Is_Intrinsic_Equality; - - --------------------------- - -- Is_Suitable_Candidate -- - --------------------------- - - function Is_Suitable_Candidate (Subp_Id : Entity_Id) return Boolean is - begin - if No (Subp_Id) then - return False; - - -- An intrinsic subprogram is never a good candidate. This is an - -- indication of a missing primitive, either defined directly or - -- inherited from a parent tagged type. - - elsif Is_Intrinsic_Subprogram (Subp_Id) then - return False; - - else - return True; - end if; - end Is_Suitable_Candidate; - - -- Local variables - - Actual_Typ : Entity_Id := Empty; - -- The actual class-wide type for Formal_Typ - - CW_Prim_OK : Boolean; - CW_Prim_Op : Entity_Id; - -- The class-wide subprogram (if available) which corresponds to the - -- renamed generic formal subprogram. - - Formal_Typ : Entity_Id := Empty; - -- The generic formal type with unknown discriminants - - Root_Prim_OK : Boolean; - Root_Prim_Op : Entity_Id; - -- The root type primitive (if available) which corresponds to the - -- renamed generic formal subprogram. - - Root_Typ : Entity_Id := Empty; - -- The root type of Actual_Typ - - Body_Decl : Node_Id; - Formal : Node_Id; - Prim_Op : Entity_Id; - Spec_Decl : Node_Id; - New_Spec : Node_Id; - - -- Start of processing for Build_Class_Wide_Wrapper - - begin - -- Analyze the specification of the renaming in case the generation - -- of the class-wide wrapper fails. - - Ren_Id := Analyze_Subprogram_Specification (Spec); - Wrap_Id := Any_Id; - - -- Do not attempt to build a wrapper if the renaming is in error - - if Error_Posted (Nam) then - return; - end if; - - -- Analyze the renamed name, but do not resolve it. The resolution is - -- completed once a suitable subprogram is found. - - Analyze (Nam); - - -- When the renamed name denotes the intrinsic operator equals, the - -- name must be treated as overloaded. This allows for a potential - -- match against the root type's predefined equality function. - - if Is_Intrinsic_Equality (Entity (Nam)) then - Set_Is_Overloaded (Nam); - Collect_Interps (Nam); - end if; - - -- Step 1: Find the generic formal type with unknown discriminants - -- and its corresponding class-wide actual type from the renamed - -- generic formal subprogram. - - Formal := First_Formal (Formal_Spec); - while Present (Formal) loop - if Has_Unknown_Discriminants (Etype (Formal)) - and then not Is_Class_Wide_Type (Etype (Formal)) - and then Is_Class_Wide_Type (Get_Instance_Of (Etype (Formal))) - then - Formal_Typ := Etype (Formal); - Actual_Typ := Base_Type (Get_Instance_Of (Formal_Typ)); - Root_Typ := Root_Type (Actual_Typ); - exit; - end if; - - Next_Formal (Formal); - end loop; - - -- The specification of the generic formal subprogram should always - -- contain a formal type with unknown discriminants whose actual is - -- a class-wide type, otherwise this indicates a failure in routine - -- Has_Class_Wide_Actual. - - pragma Assert (Present (Formal_Typ)); - - -- Step 2: Find the proper class-wide subprogram or primitive which - -- corresponds to the renamed generic formal subprogram. - - CW_Prim_Op := Find_Primitive (Actual_Typ); - CW_Prim_OK := Is_Suitable_Candidate (CW_Prim_Op); - Root_Prim_Op := Find_Primitive (Root_Typ); - Root_Prim_OK := Is_Suitable_Candidate (Root_Prim_Op); - - -- The class-wide actual type has two subprograms which correspond to - -- the renamed generic formal subprogram: - - -- with procedure Prim_Op (Param : Formal_Typ); - - -- procedure Prim_Op (Param : Actual_Typ); -- may be inherited - -- procedure Prim_Op (Param : Actual_Typ'Class); - - -- Even though the declaration of the two subprograms is legal, a - -- call to either one is ambiguous and therefore illegal. - - if CW_Prim_OK and Root_Prim_OK then - - -- A user-defined primitive has precedence over a predefined one - - if Is_Internal (CW_Prim_Op) - and then not Is_Internal (Root_Prim_Op) - then - Prim_Op := Root_Prim_Op; - - elsif Is_Internal (Root_Prim_Op) - and then not Is_Internal (CW_Prim_Op) - then - Prim_Op := CW_Prim_Op; - - elsif CW_Prim_Op = Root_Prim_Op then - Prim_Op := Root_Prim_Op; - - -- The two subprograms are legal but the class-wide subprogram is - -- a class-wide wrapper built for a previous instantiation; the - -- wrapper has precedence. - - elsif Present (Alias (CW_Prim_Op)) - and then Is_Class_Wide_Wrapper (Ultimate_Alias (CW_Prim_Op)) - then - Prim_Op := CW_Prim_Op; - - -- Otherwise both candidate subprograms are user-defined and - -- ambiguous. - - else - Error_Msg_NE - ("ambiguous actual for generic subprogram &", - Spec, Formal_Spec); - Interpretation_Error (Root_Prim_Op); - Interpretation_Error (CW_Prim_Op); - return; - end if; - - elsif CW_Prim_OK and not Root_Prim_OK then - Prim_Op := CW_Prim_Op; - - elsif not CW_Prim_OK and Root_Prim_OK then - Prim_Op := Root_Prim_Op; - - -- An intrinsic equality may act as a suitable candidate in the case - -- of a null type extension where the parent's equality is hidden. A - -- call to an intrinsic equality is expanded as dispatching. - - elsif Present (Root_Prim_Op) - and then Is_Intrinsic_Equality (Root_Prim_Op) - then - Prim_Op := Root_Prim_Op; - - -- Otherwise there are no candidate subprograms. Let the caller - -- diagnose the error. - - else - return; - end if; - - -- At this point resolution has taken place and the name is no longer - -- overloaded. Mark the primitive as referenced. - - Set_Is_Overloaded (Name (N), False); - Set_Referenced (Prim_Op); - - -- Do not generate a wrapper when the only candidate is a class-wide - -- subprogram. Instead modify the renaming to directly map the actual - -- to the generic formal. - - if CW_Prim_OK and then Prim_Op = CW_Prim_Op then - Wrap_Id := Prim_Op; - Rewrite (Nam, New_Occurrence_Of (Prim_Op, Loc)); - return; - end if; - - -- Step 3: Create the declaration and the body of the wrapper, insert - -- all the pieces into the tree. - - -- In GNATprove mode, create a function wrapper in the form of an - -- expression function, so that an implicit postcondition relating - -- the result of calling the wrapper function and the result of the - -- dispatching call to the wrapped function is known during proof. - - if GNATprove_Mode - and then Ekind (Ren_Id) in E_Function | E_Operator - then - New_Spec := Build_Spec (Ren_Id); - Body_Decl := - Make_Expression_Function (Loc, - Specification => New_Spec, - Expression => - Build_Expr_Fun_Call - (Subp_Id => Prim_Op, - Params => Parameter_Specifications (New_Spec))); - - Wrap_Id := Defining_Entity (Body_Decl); - - -- Otherwise, create separate spec and body for the subprogram - - else - Spec_Decl := - Make_Subprogram_Declaration (Loc, - Specification => Build_Spec (Ren_Id)); - Insert_Before_And_Analyze (N, Spec_Decl); - - Wrap_Id := Defining_Entity (Spec_Decl); - - Body_Decl := - Make_Subprogram_Body (Loc, - Specification => Build_Spec (Ren_Id), - Declarations => New_List, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List ( - Build_Call - (Subp_Id => Prim_Op, - Params => - Parameter_Specifications - (Specification (Spec_Decl)))))); - - Set_Corresponding_Body (Spec_Decl, Defining_Entity (Body_Decl)); - end if; - - Set_Is_Class_Wide_Wrapper (Wrap_Id); - - -- If the operator carries an Eliminated pragma, indicate that the - -- wrapper is also to be eliminated, to prevent spurious error when - -- using gnatelim on programs that include box-initialization of - -- equality operators. - - Set_Is_Eliminated (Wrap_Id, Is_Eliminated (Prim_Op)); - - -- In GNATprove mode, insert the body in the tree for analysis - - if GNATprove_Mode then - Insert_Before_And_Analyze (N, Body_Decl); - end if; - - -- The generated body does not freeze and must be analyzed when the - -- class-wide wrapper is frozen. The body is only needed if expansion - -- is enabled. - - if Expander_Active then - Append_Freeze_Action (Wrap_Id, Body_Decl); - end if; - - -- Step 4: The subprogram renaming aliases the wrapper - - Rewrite (Nam, New_Occurrence_Of (Wrap_Id, Loc)); - end Build_Class_Wide_Wrapper; - -------------------------- -- Check_Null_Exclusion -- -------------------------- @@ -2919,6 +2351,703 @@ package body Sem_Ch8 is return False; end Has_Class_Wide_Actual; + ------------------------------------------ + -- Handle_Instance_With_Class_Wide_Type -- + ------------------------------------------ + + procedure Handle_Instance_With_Class_Wide_Type + (Inst_Node : Node_Id; + Ren_Id : Entity_Id; + Wrapped_Prim : out Entity_Id; + Wrap_Id : out Entity_Id) + is + procedure Build_Class_Wide_Wrapper + (Ren_Id : Entity_Id; + Prim_Op : Entity_Id; + Wrap_Id : out Entity_Id); + -- Build a wrapper for the renaming Ren_Id of subprogram Prim_Op. + + procedure Find_Suitable_Candidate + (Prim_Op : out Entity_Id; + Is_CW_Prim : out Boolean); + -- Look for a suitable primitive to be wrapped (Prim_Op); Is_CW_Prim + -- indicates that the found candidate is a class-wide primitive (to + -- help the caller decide if the wrapper is required). + + ------------------------------ + -- Build_Class_Wide_Wrapper -- + ------------------------------ + + procedure Build_Class_Wide_Wrapper + (Ren_Id : Entity_Id; + Prim_Op : Entity_Id; + Wrap_Id : out Entity_Id) + is + Loc : constant Source_Ptr := Sloc (N); + + function Build_Call + (Subp_Id : Entity_Id; + Params : List_Id) return Node_Id; + -- Create a dispatching call to invoke routine Subp_Id with + -- actuals built from the parameter specifications of list Params. + + function Build_Expr_Fun_Call + (Subp_Id : Entity_Id; + Params : List_Id) return Node_Id; + -- Create a dispatching call to invoke function Subp_Id with + -- actuals built from the parameter specifications of list Params. + -- Directly return the call, so that it can be used inside an + -- expression function. This is a requirement of GNATprove mode. + + function Build_Spec (Subp_Id : Entity_Id) return Node_Id; + -- Create a subprogram specification based on the subprogram + -- profile of Subp_Id. + + ---------------- + -- Build_Call -- + ---------------- + + function Build_Call + (Subp_Id : Entity_Id; + Params : List_Id) return Node_Id + is + Actuals : constant List_Id := New_List; + Call_Ref : constant Node_Id := New_Occurrence_Of (Subp_Id, Loc); + Formal : Node_Id; + + begin + -- Build the actual parameters of the call + + Formal := First (Params); + while Present (Formal) loop + Append_To (Actuals, + Make_Identifier (Loc, + Chars (Defining_Identifier (Formal)))); + Next (Formal); + end loop; + + -- Generate: + -- return Subp_Id (Actuals); + + if Ekind (Subp_Id) in E_Function | E_Operator then + return + Make_Simple_Return_Statement (Loc, + Expression => + Make_Function_Call (Loc, + Name => Call_Ref, + Parameter_Associations => Actuals)); + + -- Generate: + -- Subp_Id (Actuals); + + else + return + Make_Procedure_Call_Statement (Loc, + Name => Call_Ref, + Parameter_Associations => Actuals); + end if; + end Build_Call; + + ------------------------- + -- Build_Expr_Fun_Call -- + ------------------------- + + function Build_Expr_Fun_Call + (Subp_Id : Entity_Id; + Params : List_Id) return Node_Id + is + Actuals : constant List_Id := New_List; + Call_Ref : constant Node_Id := New_Occurrence_Of (Subp_Id, Loc); + Formal : Node_Id; + + begin + pragma Assert (Ekind (Subp_Id) in E_Function | E_Operator); + + -- Build the actual parameters of the call + + Formal := First (Params); + while Present (Formal) loop + Append_To (Actuals, + Make_Identifier (Loc, + Chars (Defining_Identifier (Formal)))); + Next (Formal); + end loop; + + -- Generate: + -- Subp_Id (Actuals); + + return + Make_Function_Call (Loc, + Name => Call_Ref, + Parameter_Associations => Actuals); + end Build_Expr_Fun_Call; + + ---------------- + -- Build_Spec -- + ---------------- + + function Build_Spec (Subp_Id : Entity_Id) return Node_Id is + Params : constant List_Id := Copy_Parameter_List (Subp_Id); + Spec_Id : constant Entity_Id := + Make_Defining_Identifier (Loc, + New_External_Name (Chars (Subp_Id), 'R')); + + begin + if Ekind (Formal_Spec) = E_Procedure then + return + Make_Procedure_Specification (Loc, + Defining_Unit_Name => Spec_Id, + Parameter_Specifications => Params); + else + return + Make_Function_Specification (Loc, + Defining_Unit_Name => Spec_Id, + Parameter_Specifications => Params, + Result_Definition => + New_Copy_Tree (Result_Definition (Spec))); + end if; + end Build_Spec; + + -- Local variables + + Body_Decl : Node_Id; + Spec_Decl : Node_Id; + New_Spec : Node_Id; + + -- Start of processing for Build_Class_Wide_Wrapper + + begin + pragma Assert (not Error_Posted (Nam)); + + -- Step 1: Create the declaration and the body of the wrapper, + -- insert all the pieces into the tree. + + -- In GNATprove mode, create a function wrapper in the form of an + -- expression function, so that an implicit postcondition relating + -- the result of calling the wrapper function and the result of + -- the dispatching call to the wrapped function is known during + -- proof. + + if GNATprove_Mode + and then Ekind (Ren_Id) in E_Function | E_Operator + then + New_Spec := Build_Spec (Ren_Id); + Body_Decl := + Make_Expression_Function (Loc, + Specification => New_Spec, + Expression => + Build_Expr_Fun_Call + (Subp_Id => Prim_Op, + Params => Parameter_Specifications (New_Spec))); + + Wrap_Id := Defining_Entity (Body_Decl); + + -- Otherwise, create separate spec and body for the subprogram + + else + Spec_Decl := + Make_Subprogram_Declaration (Loc, + Specification => Build_Spec (Ren_Id)); + Insert_Before_And_Analyze (N, Spec_Decl); + + Wrap_Id := Defining_Entity (Spec_Decl); + + Body_Decl := + Make_Subprogram_Body (Loc, + Specification => Build_Spec (Ren_Id), + Declarations => New_List, + Handled_Statement_Sequence => + Make_Handled_Sequence_Of_Statements (Loc, + Statements => New_List ( + Build_Call + (Subp_Id => Prim_Op, + Params => + Parameter_Specifications + (Specification (Spec_Decl)))))); + + Set_Corresponding_Body (Spec_Decl, Defining_Entity (Body_Decl)); + end if; + + Set_Is_Class_Wide_Wrapper (Wrap_Id); + + -- If the operator carries an Eliminated pragma, indicate that + -- the wrapper is also to be eliminated, to prevent spurious + -- errors when using gnatelim on programs that include box- + -- defaulted initialization of equality operators. + + Set_Is_Eliminated (Wrap_Id, Is_Eliminated (Prim_Op)); + + -- In GNATprove mode, insert the body in the tree for analysis + + if GNATprove_Mode then + Insert_Before_And_Analyze (N, Body_Decl); + end if; + + -- The generated body does not freeze and must be analyzed when + -- the class-wide wrapper is frozen. The body is only needed if + -- expansion is enabled. + + if Expander_Active then + Append_Freeze_Action (Wrap_Id, Body_Decl); + end if; + + -- Step 2: The subprogram renaming aliases the wrapper + + Rewrite (Name (N), New_Occurrence_Of (Wrap_Id, Loc)); + end Build_Class_Wide_Wrapper; + + ----------------------------- + -- Find_Suitable_Candidate -- + ----------------------------- + + procedure Find_Suitable_Candidate + (Prim_Op : out Entity_Id; + Is_CW_Prim : out Boolean) + is + Loc : constant Source_Ptr := Sloc (N); + + function Find_Primitive (Typ : Entity_Id) return Entity_Id; + -- Find a primitive subprogram of type Typ which matches the + -- profile of the renaming declaration. + + procedure Interpretation_Error (Subp_Id : Entity_Id); + -- Emit a continuation error message suggesting subprogram Subp_Id + -- as a possible interpretation. + + function Is_Intrinsic_Equality + (Subp_Id : Entity_Id) return Boolean; + -- Determine whether subprogram Subp_Id denotes the intrinsic "=" + -- operator. + + function Is_Suitable_Candidate + (Subp_Id : Entity_Id) return Boolean; + -- Determine whether subprogram Subp_Id is a suitable candidate + -- for the role of a wrapped subprogram. + + -------------------- + -- Find_Primitive -- + -------------------- + + function Find_Primitive (Typ : Entity_Id) return Entity_Id is + procedure Replace_Parameter_Types (Spec : Node_Id); + -- Given a specification Spec, replace all class-wide parameter + -- types with reference to type Typ. + + ----------------------------- + -- Replace_Parameter_Types -- + ----------------------------- + + procedure Replace_Parameter_Types (Spec : Node_Id) is + Formal : Node_Id; + Formal_Id : Entity_Id; + Formal_Typ : Node_Id; + + begin + Formal := First (Parameter_Specifications (Spec)); + while Present (Formal) loop + Formal_Id := Defining_Identifier (Formal); + Formal_Typ := Parameter_Type (Formal); + + -- Create a new entity for each class-wide formal to + -- prevent aliasing with the original renaming. Replace + -- the type of such a parameter with the candidate type. + + if Nkind (Formal_Typ) = N_Identifier + and then Is_Class_Wide_Type (Etype (Formal_Typ)) + then + Set_Defining_Identifier (Formal, + Make_Defining_Identifier (Loc, Chars (Formal_Id))); + + Set_Parameter_Type (Formal, + New_Occurrence_Of (Typ, Loc)); + end if; + + Next (Formal); + end loop; + end Replace_Parameter_Types; + + -- Local variables + + Alt_Ren : constant Node_Id := New_Copy_Tree (N); + Alt_Nam : constant Node_Id := Name (Alt_Ren); + Alt_Spec : constant Node_Id := Specification (Alt_Ren); + Subp_Id : Entity_Id; + + -- Start of processing for Find_Primitive + + begin + -- Each attempt to find a suitable primitive of a particular + -- type operates on its own copy of the original renaming. + -- As a result the original renaming is kept decoration and + -- side-effect free. + + -- Inherit the overloaded status of the renamed subprogram name + + if Is_Overloaded (Nam) then + Set_Is_Overloaded (Alt_Nam); + Save_Interps (Nam, Alt_Nam); + end if; + + -- The copied renaming is hidden from visibility to prevent the + -- pollution of the enclosing context. + + Set_Defining_Unit_Name (Alt_Spec, Make_Temporary (Loc, 'R')); + + -- The types of all class-wide parameters must be changed to + -- the candidate type. + + Replace_Parameter_Types (Alt_Spec); + + -- Try to find a suitable primitive that matches the altered + -- profile of the renaming specification. + + Subp_Id := + Find_Renamed_Entity + (N => Alt_Ren, + Nam => Name (Alt_Ren), + New_S => Analyze_Subprogram_Specification (Alt_Spec), + Is_Actual => Is_Actual); + + -- Do not return Any_Id if the resolution of the altered + -- profile failed as this complicates further checks on + -- the caller side; return Empty instead. + + if Subp_Id = Any_Id then + return Empty; + else + return Subp_Id; + end if; + end Find_Primitive; + + -------------------------- + -- Interpretation_Error -- + -------------------------- + + procedure Interpretation_Error (Subp_Id : Entity_Id) is + begin + Error_Msg_Sloc := Sloc (Subp_Id); + + if Is_Internal (Subp_Id) then + Error_Msg_NE + ("\\possible interpretation: predefined & #", + Spec, Formal_Spec); + else + Error_Msg_NE + ("\\possible interpretation: & defined #", + Spec, Formal_Spec); + end if; + end Interpretation_Error; + + --------------------------- + -- Is_Intrinsic_Equality -- + --------------------------- + + function Is_Intrinsic_Equality (Subp_Id : Entity_Id) return Boolean + is + begin + return + Ekind (Subp_Id) = E_Operator + and then Chars (Subp_Id) = Name_Op_Eq + and then Is_Intrinsic_Subprogram (Subp_Id); + end Is_Intrinsic_Equality; + + --------------------------- + -- Is_Suitable_Candidate -- + --------------------------- + + function Is_Suitable_Candidate (Subp_Id : Entity_Id) return Boolean + is + begin + if No (Subp_Id) then + return False; + + -- An intrinsic subprogram is never a good candidate. This + -- is an indication of a missing primitive, either defined + -- directly or inherited from a parent tagged type. + + elsif Is_Intrinsic_Subprogram (Subp_Id) then + return False; + + else + return True; + end if; + end Is_Suitable_Candidate; + + -- Local variables + + Actual_Typ : Entity_Id := Empty; + -- The actual class-wide type for Formal_Typ + + CW_Prim_OK : Boolean; + CW_Prim_Op : Entity_Id; + -- The class-wide subprogram (if available) that corresponds to + -- the renamed generic formal subprogram. + + Formal_Typ : Entity_Id := Empty; + -- The generic formal type with unknown discriminants + + Root_Prim_OK : Boolean; + Root_Prim_Op : Entity_Id; + -- The root type primitive (if available) that corresponds to the + -- renamed generic formal subprogram. + + Root_Typ : Entity_Id := Empty; + -- The root type of Actual_Typ + + Formal : Node_Id; + + -- Start of processing for Find_Suitable_Candidate + + begin + pragma Assert (not Error_Posted (Nam)); + + Prim_Op := Empty; + Is_CW_Prim := False; + + -- Analyze the renamed name, but do not resolve it. The resolution + -- is completed once a suitable subprogram is found. + + Analyze (Nam); + + -- When the renamed name denotes the intrinsic operator equals, + -- the name must be treated as overloaded. This allows for a + -- potential match against the root type's predefined equality + -- function. + + if Is_Intrinsic_Equality (Entity (Nam)) then + Set_Is_Overloaded (Nam); + Collect_Interps (Nam); + end if; + + -- Step 1: Find the generic formal type and its corresponding + -- class-wide actual type from the renamed generic formal + -- subprogram. + + Formal := First_Formal (Formal_Spec); + while Present (Formal) loop + if Has_Unknown_Discriminants (Etype (Formal)) + and then not Is_Class_Wide_Type (Etype (Formal)) + and then Is_Class_Wide_Type (Get_Instance_Of (Etype (Formal))) + then + Formal_Typ := Etype (Formal); + Actual_Typ := Base_Type (Get_Instance_Of (Formal_Typ)); + Root_Typ := Root_Type (Actual_Typ); + exit; + end if; + + Next_Formal (Formal); + end loop; + + -- The specification of the generic formal subprogram should + -- always contain a formal type with unknown discriminants whose + -- actual is a class-wide type; otherwise this indicates a failure + -- in function Has_Class_Wide_Actual. + + pragma Assert (Present (Formal_Typ)); + + -- Step 2: Find the proper class-wide subprogram or primitive + -- that corresponds to the renamed generic formal subprogram. + + CW_Prim_Op := Find_Primitive (Actual_Typ); + CW_Prim_OK := Is_Suitable_Candidate (CW_Prim_Op); + Root_Prim_Op := Find_Primitive (Root_Typ); + Root_Prim_OK := Is_Suitable_Candidate (Root_Prim_Op); + + -- The class-wide actual type has two subprograms that correspond + -- to the renamed generic formal subprogram: + + -- with procedure Prim_Op (Param : Formal_Typ); + + -- procedure Prim_Op (Param : Actual_Typ); -- may be inherited + -- procedure Prim_Op (Param : Actual_Typ'Class); + + -- Even though the declaration of the two subprograms is legal, a + -- call to either one is ambiguous and therefore illegal. + + if CW_Prim_OK and Root_Prim_OK then + + -- A user-defined primitive has precedence over a predefined + -- one. + + if Is_Internal (CW_Prim_Op) + and then not Is_Internal (Root_Prim_Op) + then + Prim_Op := Root_Prim_Op; + + elsif Is_Internal (Root_Prim_Op) + and then not Is_Internal (CW_Prim_Op) + then + Prim_Op := CW_Prim_Op; + Is_CW_Prim := True; + + elsif CW_Prim_Op = Root_Prim_Op then + Prim_Op := Root_Prim_Op; + + -- The two subprograms are legal but the class-wide subprogram + -- is a class-wide wrapper built for a previous instantiation; + -- the wrapper has precedence. + + elsif Present (Alias (CW_Prim_Op)) + and then Is_Class_Wide_Wrapper (Ultimate_Alias (CW_Prim_Op)) + then + Prim_Op := CW_Prim_Op; + Is_CW_Prim := True; + + -- Otherwise both candidate subprograms are user-defined and + -- ambiguous. + + else + Error_Msg_NE + ("ambiguous actual for generic subprogram &", + Spec, Formal_Spec); + Interpretation_Error (Root_Prim_Op); + Interpretation_Error (CW_Prim_Op); + return; + end if; + + elsif CW_Prim_OK and not Root_Prim_OK then + Prim_Op := CW_Prim_Op; + Is_CW_Prim := True; + + elsif not CW_Prim_OK and Root_Prim_OK then + Prim_Op := Root_Prim_Op; + + -- An intrinsic equality may act as a suitable candidate in the + -- case of a null type extension where the parent's equality + -- is hidden. A call to an intrinsic equality is expanded as + -- dispatching. + + elsif Present (Root_Prim_Op) + and then Is_Intrinsic_Equality (Root_Prim_Op) + then + Prim_Op := Root_Prim_Op; + + -- Otherwise there are no candidate subprograms. Let the caller + -- diagnose the error. + + else + return; + end if; + + -- At this point resolution has taken place and the name is no + -- longer overloaded. Mark the primitive as referenced. + + Set_Is_Overloaded (Name (N), False); + Set_Referenced (Prim_Op); + end Find_Suitable_Candidate; + + -- Local variables + + Is_CW_Prim : Boolean; + + -- Start of processing for Handle_Instance_With_Class_Wide_Type + + begin + Wrapped_Prim := Empty; + Wrap_Id := Empty; + + -- Ada 2012 (AI05-0071): A generic/instance scenario involving a + -- formal type with unknown discriminants and a generic primitive + -- operation of the said type with a box require special processing + -- when the actual is a class-wide type: + -- + -- generic + -- type Formal_Typ (<>) is private; + -- with procedure Prim_Op (Param : Formal_Typ) is <>; + -- package Gen is ... + -- + -- package Inst is new Gen (Actual_Typ'Class); + -- + -- In this case the general renaming mechanism used in the prologue + -- of an instance no longer applies: + -- + -- procedure Prim_Op (Param : Formal_Typ) renames Prim_Op; + -- + -- The above is replaced the following wrapper/renaming combination: + -- + -- procedure Wrapper (Param : Formal_Typ) is -- wrapper + -- begin + -- Prim_Op (Param); -- primitive + -- end Wrapper; + -- + -- procedure Prim_Op (Param : Formal_Typ) renames Wrapper; + -- + -- This transformation applies only if there is no explicit visible + -- class-wide operation at the point of the instantiation. Ren_Id is + -- the entity of the renaming declaration. When the transformation + -- applies, Wrapped_Prim is the entity of the wrapped primitive. + + if Box_Present (Inst_Node) then + Find_Suitable_Candidate + (Prim_Op => Wrapped_Prim, + Is_CW_Prim => Is_CW_Prim); + + if Present (Wrapped_Prim) then + if not Is_CW_Prim then + Build_Class_Wide_Wrapper (Ren_Id, Wrapped_Prim, Wrap_Id); + + -- Small optimization: When the candidate is a class-wide + -- subprogram we don't build the wrapper; we modify the + -- renaming declaration to directly map the actual to the + -- generic formal and discard the candidate. + + else + Rewrite (Nam, New_Occurrence_Of (Wrapped_Prim, Sloc (N))); + Wrapped_Prim := Empty; + end if; + end if; + + -- Ada 2022 (AI12-0165, RM 12.6(8.5/3)): The actual subprogram for a + -- formal_abstract_subprogram_declaration shall be: + -- a) a dispatching operation of the controlling type; or + -- b) if the controlling type is a formal type, and the actual + -- type corresponding to that formal type is a specific type T, + -- a dispatching operation of type T; or + -- c) if the controlling type is a formal type, and the actual + -- type is a class-wide type T'Class, an implicitly declared + -- subprogram corresponding to a primitive operation of type T. + + elsif Nkind (Inst_Node) = N_Formal_Abstract_Subprogram_Declaration + and then Is_Entity_Name (Nam) + then + Find_Suitable_Candidate + (Prim_Op => Wrapped_Prim, + Is_CW_Prim => Is_CW_Prim); + + if Present (Wrapped_Prim) then + + -- Cases (a) and (b); see previous description. + + if not Is_CW_Prim then + Build_Class_Wide_Wrapper (Ren_Id, Wrapped_Prim, Wrap_Id); + + -- Case (c); see previous description. + + -- Implicit operations of T'Class for subtype declarations + -- are built by Derive_Subprogram, and their Alias attribute + -- references the primitive operation of T. + + elsif not Comes_From_Source (Wrapped_Prim) + and then Nkind (Parent (Wrapped_Prim)) = N_Subtype_Declaration + and then Present (Alias (Wrapped_Prim)) + then + -- We don't need to build the wrapper; we modify the + -- renaming declaration to directly map the actual to + -- the generic formal and discard the candidate. + + Rewrite (Nam, + New_Occurrence_Of (Alias (Wrapped_Prim), Sloc (N))); + Wrapped_Prim := Empty; + + -- Legality rules do not apply; discard the candidate. + + else + Wrapped_Prim := Empty; + end if; + end if; + end if; + end Handle_Instance_With_Class_Wide_Type; + ------------------------- -- Original_Subprogram -- ------------------------- @@ -2965,12 +3094,13 @@ package body Sem_Ch8 is -- Local variables CW_Actual : constant Boolean := Has_Class_Wide_Actual; - -- Ada 2012 (AI05-071, AI05-0131): True if the renaming is for a - -- defaulted formal subprogram when the actual for a related formal - -- type is class-wide. + -- Ada 2012 (AI05-071, AI05-0131) and Ada 2022 (AI12-0165): True if the + -- renaming is for a defaulted formal subprogram when the actual for a + -- related formal type is class-wide. - Inst_Node : Node_Id := Empty; - New_S : Entity_Id; + Inst_Node : Node_Id := Empty; + New_S : Entity_Id; + Wrapped_Prim : Entity_Id := Empty; -- Start of processing for Analyze_Subprogram_Renaming @@ -3101,11 +3231,64 @@ package body Sem_Ch8 is if Is_Actual then Inst_Node := Unit_Declaration_Node (Formal_Spec); - -- Check whether the renaming is for a defaulted actual subprogram - -- with a class-wide actual. + -- Ada 2012 (AI05-0071) and Ada 2022 (AI12-0165): when the actual + -- type is a class-wide type T'Class we may need to wrap a primitive + -- operation of T. Search for the wrapped primitive and (if required) + -- build a wrapper whose body consists of a dispatching call to the + -- wrapped primitive of T, with its formal parameters as the actual + -- parameters. + + if CW_Actual and then + + -- Ada 2012 (AI05-0071): Check whether the renaming is for a + -- defaulted actual subprogram with a class-wide actual. + + (Box_Present (Inst_Node) + + or else + + -- Ada 2022 (AI12-0165): Check whether the renaming is for a formal + -- abstract subprogram declaration with a class-wide actual. + + (Nkind (Inst_Node) = N_Formal_Abstract_Subprogram_Declaration + and then Is_Entity_Name (Nam))) + then + New_S := Analyze_Subprogram_Specification (Spec); + + -- Do not attempt to build the wrapper if the renaming is in error + + if not Error_Posted (Nam) then + Handle_Instance_With_Class_Wide_Type + (Inst_Node => Inst_Node, + Ren_Id => New_S, + Wrapped_Prim => Wrapped_Prim, + Wrap_Id => Old_S); + + -- If several candidates were found, then we reported the + -- ambiguity; stop processing the renaming declaration to + -- avoid reporting further (spurious) errors. + + if Error_Posted (Spec) then + return; + end if; + + end if; + end if; + + if Present (Wrapped_Prim) then + + -- When the wrapper is built, the subprogram renaming aliases + -- the wrapper. - if CW_Actual and then Box_Present (Inst_Node) then - Build_Class_Wide_Wrapper (New_S, Old_S); + Analyze (Nam); + + pragma Assert (Old_S = Entity (Nam) + and then Is_Class_Wide_Wrapper (Old_S)); + + -- The subprogram renaming declaration may become Ghost if it + -- renames a wrapper of a Ghost entity. + + Mark_Ghost_Renaming (N, Wrapped_Prim); elsif Is_Entity_Name (Nam) and then Present (Entity (Nam)) @@ -3685,7 +3868,15 @@ package body Sem_Ch8 is -- indicate that the renaming is an abstract dispatching operation -- with a controlling type. - if Is_Actual and then Is_Abstract_Subprogram (Formal_Spec) then + -- Skip this decoration when the renaming corresponds to an + -- association with class-wide wrapper (see above) because such + -- wrapper is neither abstract nor a dispatching operation (its + -- body has the dispatching call to the wrapped primitive). + + if Is_Actual + and then Is_Abstract_Subprogram (Formal_Spec) + and then No (Wrapped_Prim) + then -- Mark the renaming as abstract here, so Find_Dispatching_Type -- see it as corresponding to a generic association for a |