(root)/
gcc-13.2.0/
gcc/
ada/
accessibility.adb
------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                        A C C E S S I B I L I T Y                         --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--          Copyright (C) 2022-2023, Free Software Foundation, Inc.         --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
-- for  more details.  You should have  received  a copy of the GNU General --
-- Public License  distributed with GNAT; see file COPYING3.  If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license.          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

with Atree;          use Atree;
with Checks;         use Checks;
with Debug;          use Debug;
with Einfo;          use Einfo;
with Einfo.Entities; use Einfo.Entities;
with Elists;         use Elists;
with Errout;         use Errout;
with Einfo.Utils;    use Einfo.Utils;
with Exp_Atag;       use Exp_Atag;
with Exp_Ch3;        use Exp_Ch3;
with Exp_Ch7;        use Exp_Ch7;
with Exp_Tss;        use Exp_Tss;
with Exp_Util;       use Exp_Util;
with Namet;          use Namet;
with Nlists;         use Nlists;
with Nmake;          use Nmake;
with Opt;            use Opt;
with Restrict;       use Restrict;
with Rtsfind;        use Rtsfind;
with Sem;            use Sem;
with Sem_Aux;        use Sem_Aux;
with Sem_Ch8;        use Sem_Ch8;
with Sem_Res;        use Sem_Res;
with Sem_Util;       use Sem_Util;
with Sinfo;          use Sinfo;
with Sinfo.Nodes;    use Sinfo.Nodes;
with Sinfo.Utils;    use Sinfo.Utils;
with Snames;         use Snames;
with Stand;          use Stand;
with Tbuild;         use Tbuild;

package body Accessibility is

   ---------------------------
   -- Accessibility_Message --
   ---------------------------

   procedure Accessibility_Message (N : Node_Id; Typ : Entity_Id) is
      Loc   : constant Source_Ptr := Sloc (N);
      P     : constant Node_Id    := Prefix (N);
      Indic : Node_Id             := Parent (Parent (N));

   begin
      --  In an instance, this is a runtime check, but one we know will fail,
      --  so generate an appropriate warning.

      if In_Instance_Body then
         Error_Msg_Warn := SPARK_Mode /= On;
         Error_Msg_F
           ("non-local pointer cannot point to local object<<", P);
         Error_Msg_F ("\Program_Error [<<", P);
         Rewrite (N,
           Make_Raise_Program_Error (Loc,
             Reason => PE_Accessibility_Check_Failed));
         Set_Etype (N, Typ);
         return;

      else
         Error_Msg_F ("non-local pointer cannot point to local object", P);

         --  Check for case where we have a missing access definition

         if Is_Record_Type (Current_Scope)
           and then
             Nkind (Parent (N)) in N_Discriminant_Association
                                 | N_Index_Or_Discriminant_Constraint
         then
            Indic := Parent (Parent (N));
            while Present (Indic)
              and then Nkind (Indic) /= N_Subtype_Indication
            loop
               Indic := Parent (Indic);
            end loop;

            if Present (Indic) then
               Error_Msg_NE
                 ("\use an access definition for" &
                  " the access discriminant of&",
                  N, Entity (Subtype_Mark (Indic)));
            end if;
         end if;
      end if;
   end Accessibility_Message;

   -------------------------
   -- Accessibility_Level --
   -------------------------

   function Accessibility_Level
     (Expr              : Node_Id;
      Level             : Accessibility_Level_Kind;
      In_Return_Context : Boolean := False;
      Allow_Alt_Model   : Boolean := True) return Node_Id
   is
      Loc : constant Source_Ptr := Sloc (Expr);

      function Accessibility_Level (Expr : Node_Id) return Node_Id
        is (Accessibility_Level (Expr, Level, In_Return_Context));
      --  Renaming of the enclosing function to facilitate recursive calls

      function Make_Level_Literal (Level : Uint) return Node_Id;
      --  Construct an integer literal representing an accessibility level with
      --  its type set to Natural.

      function Innermost_Master_Scope_Depth (N : Node_Id) return Uint;
      --  Returns the scope depth of the given node's innermost enclosing scope
      --  (effectively the accessibility level of the innermost enclosing
      --  master).

      function Function_Call_Or_Allocator_Level (N : Node_Id) return Node_Id;
      --  Centralized processing of subprogram calls which may appear in prefix
      --  notation.

      function Typ_Access_Level (Typ : Entity_Id) return Uint
        is (Type_Access_Level (Typ, Allow_Alt_Model));
      --  Renaming of Type_Access_Level with Allow_Alt_Model specified to avoid
      --  passing the parameter specifically in every call.

      ----------------------------------
      -- Innermost_Master_Scope_Depth --
      ----------------------------------

      function Innermost_Master_Scope_Depth (N : Node_Id) return Uint is
         Encl_Scop           : Entity_Id;
         Ent                 : Entity_Id;
         Node_Par            : Node_Id := Parent (N);
         Master_Lvl_Modifier : Int     := 0;

      begin
         --  Locate the nearest enclosing node (by traversing Parents)
         --  that Defining_Entity can be applied to, and return the
         --  depth of that entity's nearest enclosing scope.

         --  The RM 7.6.1(3) definition of "master" includes statements
         --  and conditions for loops among other things. Are these cases
         --  detected properly ???

         while Present (Node_Par) loop
            Ent := Defining_Entity_Or_Empty (Node_Par);

            if Present (Ent) then
               Encl_Scop := Find_Enclosing_Scope (Ent);

               --  Ignore transient scopes made during expansion while also
               --  taking into account certain expansions - like iterators
               --  which get expanded into renamings and thus not marked
               --  as coming from source.

               if Comes_From_Source (Node_Par)
                 or else (Nkind (Node_Par) = N_Object_Renaming_Declaration
                           and then Comes_From_Iterator (Node_Par))
               then
                  --  Note that in some rare cases the scope depth may not be
                  --  set, for example, when we are in the middle of analyzing
                  --  a type and the enclosing scope is said type. So, instead,
                  --  continue to move up the parent chain since the scope
                  --  depth of the type's parent is the same as that of the
                  --  type.

                  if not Scope_Depth_Set (Encl_Scop) then
                     pragma Assert (Nkind (Parent (Encl_Scop))
                                     = N_Full_Type_Declaration);
                  else
                     return
                       Scope_Depth (Encl_Scop) + Master_Lvl_Modifier;
                  end if;
               end if;

            --  For a return statement within a function, return
            --  the depth of the function itself. This is not just
            --  a small optimization, but matters when analyzing
            --  the expression in an expression function before
            --  the body is created.

            elsif Nkind (Node_Par) in N_Extended_Return_Statement
                                    | N_Simple_Return_Statement
            then
               return Scope_Depth (Enclosing_Subprogram (Node_Par));

            --  Statements are counted as masters

            elsif Is_Master (Node_Par) then
               Master_Lvl_Modifier := Master_Lvl_Modifier + 1;

            end if;

            Node_Par := Parent (Node_Par);
         end loop;

         --  Should never reach the following return

         pragma Assert (False);

         return Scope_Depth (Current_Scope) + 1;
      end Innermost_Master_Scope_Depth;

      ------------------------
      -- Make_Level_Literal --
      ------------------------

      function Make_Level_Literal (Level : Uint) return Node_Id is
         Result : constant Node_Id := Make_Integer_Literal (Loc, Level);

      begin
         Set_Etype (Result, Standard_Natural);
         return Result;
      end Make_Level_Literal;

      --------------------------------------
      -- Function_Call_Or_Allocator_Level --
      --------------------------------------

      function Function_Call_Or_Allocator_Level (N : Node_Id) return Node_Id is
         Par      : Node_Id;
         Prev_Par : Node_Id;
      begin
         --  Results of functions are objects, so we either get the
         --  accessibility of the function or, in case of a call which is
         --  indirect, the level of the access-to-subprogram type.

         --  This code looks wrong ???

         if Nkind (N) = N_Function_Call
           and then Ada_Version < Ada_2005
         then
            if Is_Entity_Name (Name (N)) then
               return Make_Level_Literal
                        (Subprogram_Access_Level (Entity (Name (N))));
            else
               return Make_Level_Literal
                        (Typ_Access_Level (Etype (Prefix (Name (N)))));
            end if;

         --  We ignore coextensions as they cannot be implemented under the
         --  "small-integer" model.

         elsif Nkind (N) = N_Allocator
           and then (Is_Static_Coextension (N)
                      or else Is_Dynamic_Coextension (N))
         then
            return Make_Level_Literal (Scope_Depth (Standard_Standard));
         end if;

         --  Named access types have a designated level

         if Is_Named_Access_Type (Etype (N)) then
            return Make_Level_Literal (Typ_Access_Level (Etype (N)));

         --  Otherwise, the level is dictated by RM 3.10.2 (10.7/3)

         else
            --  Check No_Dynamic_Accessibility_Checks restriction override for
            --  alternative accessibility model.

            if Allow_Alt_Model
              and then No_Dynamic_Accessibility_Checks_Enabled (N)
              and then Is_Anonymous_Access_Type (Etype (N))
            then
               --  In the alternative model the level is that of the
               --  designated type.

               if Debug_Flag_Underscore_B then
                  return Make_Level_Literal (Typ_Access_Level (Etype (N)));

               --  For function calls the level is that of the innermost
               --  master, otherwise (for allocators etc.) we get the level
               --  of the corresponding anonymous access type, which is
               --  calculated through the normal path of execution.

               elsif Nkind (N) = N_Function_Call then
                  return Make_Level_Literal
                           (Innermost_Master_Scope_Depth (Expr));
               end if;
            end if;

            if Nkind (N) = N_Function_Call then
               --  Dynamic checks are generated when we are within a return
               --  value or we are in a function call within an anonymous
               --  access discriminant constraint of a return object (signified
               --  by In_Return_Context) on the side of the callee.

               --  So, in this case, return accessibility level of the
               --  enclosing subprogram.

               if In_Return_Value (N)
                 or else In_Return_Context
               then
                  return Make_Level_Literal
                           (Subprogram_Access_Level (Current_Subprogram));
               end if;
            end if;

            --  When the call is being dereferenced the level is that of the
            --  enclosing master of the dereferenced call.

            if Nkind (Parent (N)) in N_Explicit_Dereference
                                   | N_Indexed_Component
                                   | N_Selected_Component
            then
               return Make_Level_Literal
                        (Innermost_Master_Scope_Depth (Expr));
            end if;

            --  Find any relevant enclosing parent nodes that designate an
            --  object being initialized.

            --  Note: The above is only relevant if the result is used "in its
            --  entirety" as RM 3.10.2 (10.2/3) states. However, this is
            --  accounted for in the case statement in the main body of
            --  Accessibility_Level for N_Selected_Component.

            Par      := Parent (Expr);
            Prev_Par := Empty;
            while Present (Par) loop
               --  Detect an expanded implicit conversion, typically this
               --  occurs on implicitly converted actuals in calls.

               --  Does this catch all implicit conversions ???

               if Nkind (Par) = N_Type_Conversion
                 and then Is_Named_Access_Type (Etype (Par))
               then
                  return Make_Level_Literal
                           (Typ_Access_Level (Etype (Par)));
               end if;

               --  Jump out when we hit an object declaration or the right-hand
               --  side of an assignment, or a construct such as an aggregate
               --  subtype indication which would be the result is not used
               --  "in its entirety."

               exit when Nkind (Par) in N_Object_Declaration
                           or else (Nkind (Par) = N_Assignment_Statement
                                     and then Name (Par) /= Prev_Par);

               Prev_Par := Par;
               Par      := Parent (Par);
            end loop;

            --  Assignment statements are handled in a similar way in
            --  accordance to the left-hand part. However, strictly speaking,
            --  this is illegal according to the RM, but this change is needed
            --  to pass an ACATS C-test and is useful in general ???

            case Nkind (Par) is
               when N_Object_Declaration =>
                  return Make_Level_Literal
                           (Scope_Depth
                             (Scope (Defining_Identifier (Par))));

               when N_Assignment_Statement =>
                  --  Return the accessibility level of the left-hand part

                  return Accessibility_Level
                           (Expr              => Name (Par),
                            Level             => Object_Decl_Level,
                            In_Return_Context => In_Return_Context);

               when others =>
                  return Make_Level_Literal
                           (Innermost_Master_Scope_Depth (Expr));
            end case;
         end if;
      end Function_Call_Or_Allocator_Level;

      --  Local variables

      E   : Node_Id := Original_Node (Expr);
      Pre : Node_Id;

   --  Start of processing for Accessibility_Level

   begin
      --  We could be looking at a reference to a formal due to the expansion
      --  of entries and other cases, so obtain the renaming if necessary.

      if Present (Param_Entity (Expr)) then
         E := Param_Entity (Expr);
      end if;

      --  Extract the entity

      if Nkind (E) in N_Has_Entity and then Present (Entity (E)) then
         E := Entity (E);

         --  Deal with a possible renaming of a private protected component

         if Ekind (E) in E_Constant | E_Variable and then Is_Prival (E) then
            E := Prival_Link (E);
         end if;
      end if;

      --  Perform the processing on the expression

      case Nkind (E) is
         --  The level of an aggregate is that of the innermost master that
         --  evaluates it as defined in RM 3.10.2 (10/4).

         when N_Aggregate =>
            return Make_Level_Literal (Innermost_Master_Scope_Depth (Expr));

         --  The accessibility level is that of the access type, except for an
         --  anonymous allocators which have special rules defined in RM 3.10.2
         --  (14/3).

         when N_Allocator =>
            return Function_Call_Or_Allocator_Level (E);

         --  We could reach this point for two reasons. Either the expression
         --  applies to a special attribute ('Loop_Entry, 'Result, or 'Old), or
         --  we are looking at the access attributes directly ('Access,
         --  'Address, or 'Unchecked_Access).

         when N_Attribute_Reference =>
            Pre := Original_Node (Prefix (E));

            --  Regular 'Access attribute presence means we have to look at the
            --  prefix.

            if Attribute_Name (E) = Name_Access then
               return Accessibility_Level (Prefix (E));

            --  Unchecked or unrestricted attributes have unlimited depth

            elsif Attribute_Name (E) in Name_Address
                                      | Name_Unchecked_Access
                                      | Name_Unrestricted_Access
            then
               return Make_Level_Literal (Scope_Depth (Standard_Standard));

            --  'Access can be taken further against other special attributes,
            --  so handle these cases explicitly.

            elsif Attribute_Name (E)
                    in Name_Old | Name_Loop_Entry | Name_Result
            then
               --  Named access types

               if Is_Named_Access_Type (Etype (Pre)) then
                  return Make_Level_Literal
                           (Typ_Access_Level (Etype (Pre)));

               --  Anonymous access types

               elsif Nkind (Pre) in N_Has_Entity
                 and then Ekind (Entity (Pre)) not in Subprogram_Kind
                 and then Present (Get_Dynamic_Accessibility (Entity (Pre)))
                 and then Level = Dynamic_Level
               then
                  return New_Occurrence_Of
                           (Get_Dynamic_Accessibility (Entity (Pre)), Loc);

               --  Otherwise the level is treated in a similar way as
               --  aggregates according to RM 6.1.1 (35.1/4) which concerns
               --  an implicit constant declaration - in turn defining the
               --  accessibility level to be that of the implicit constant
               --  declaration.

               else
                  return Make_Level_Literal
                           (Innermost_Master_Scope_Depth (Expr));
               end if;

            else
               raise Program_Error;
            end if;

         --  This is the "base case" for accessibility level calculations which
         --  means we are near the end of our recursive traversal.

         when N_Defining_Identifier =>
            --  A dynamic check is performed on the side of the callee when we
            --  are within a return statement, so return a library-level
            --  accessibility level to null out checks on the side of the
            --  caller.

            if Is_Explicitly_Aliased (E)
              and then (In_Return_Context
                         or else (Level /= Dynamic_Level
                                   and then In_Return_Value (Expr)))
            then
               return Make_Level_Literal (Scope_Depth (Standard_Standard));

            --  Something went wrong and an extra accessibility formal has not
            --  been generated when one should have ???

            elsif Is_Formal (E)
              and then No (Get_Dynamic_Accessibility (E))
              and then Ekind (Etype (E)) = E_Anonymous_Access_Type
            then
               return Make_Level_Literal (Scope_Depth (Standard_Standard));

            --  Stand-alone object of an anonymous access type "SAOAAT"

            elsif (Is_Formal (E)
                    or else Ekind (E) in E_Variable
                                       | E_Constant)
              and then Present (Get_Dynamic_Accessibility (E))
              and then (Level = Dynamic_Level
                         or else Level = Zero_On_Dynamic_Level)
            then
               if Level = Zero_On_Dynamic_Level then
                  return Make_Level_Literal
                           (Scope_Depth (Standard_Standard));
               end if;

               --  No_Dynamic_Accessibility_Checks restriction override for
               --  alternative accessibility model.

               if Allow_Alt_Model
                 and then No_Dynamic_Accessibility_Checks_Enabled (E)
               then
                  --  In the alternative model the level is that of the
                  --  designated type entity's context.

                  if Debug_Flag_Underscore_B then
                     return Make_Level_Literal (Typ_Access_Level (Etype (E)));

                  --  Otherwise the level depends on the entity's context

                  elsif Is_Formal (E) then
                     return Make_Level_Literal
                              (Subprogram_Access_Level
                                (Enclosing_Subprogram (E)));
                  else
                     return Make_Level_Literal
                              (Scope_Depth (Enclosing_Dynamic_Scope (E)));
                  end if;
               end if;

               --  Return the dynamic level in the normal case

               return New_Occurrence_Of
                        (Get_Dynamic_Accessibility (E), Loc);

            --  Initialization procedures have a special extra accessibility
            --  parameter associated with the level at which the object
            --  being initialized exists

            elsif Ekind (E) = E_Record_Type
              and then Is_Limited_Record (E)
              and then Current_Scope = Init_Proc (E)
              and then Present (Init_Proc_Level_Formal (Current_Scope))
            then
               return New_Occurrence_Of
                        (Init_Proc_Level_Formal (Current_Scope), Loc);

            --  Current instance of the type is deeper than that of the type
            --  according to RM 3.10.2 (21).

            elsif Is_Type (E) then
               --  When restriction No_Dynamic_Accessibility_Checks is active
               --  along with -gnatd_b.

               if Allow_Alt_Model
                 and then No_Dynamic_Accessibility_Checks_Enabled (E)
                 and then Debug_Flag_Underscore_B
               then
                  return Make_Level_Literal (Typ_Access_Level (E));
               end if;

               --  Normal path

               return Make_Level_Literal (Typ_Access_Level (E) + 1);

            --  Move up the renamed entity or object if it came from source
            --  since expansion may have created a dummy renaming under
            --  certain circumstances.

            --  Note: We check if the original node of the renaming comes
            --  from source because the node may have been rewritten.

            elsif Present (Renamed_Entity_Or_Object (E))
              and then Comes_From_Source
                (Original_Node (Renamed_Entity_Or_Object (E)))
            then
               return Accessibility_Level (Renamed_Entity_Or_Object (E));

            --  Named access types get their level from their associated type

            elsif Is_Named_Access_Type (Etype (E)) then
               return Make_Level_Literal
                        (Typ_Access_Level (Etype (E)));

            --  Check if E is an expansion-generated renaming of an iterator
            --  by examining Related_Expression. If so, determine the
            --  accessibility level based on the original expression.

            elsif Ekind (E) in E_Constant | E_Variable
              and then Present (Related_Expression (E))
            then
               return Accessibility_Level (Related_Expression (E));

            elsif Level = Dynamic_Level
               and then Ekind (E) in E_In_Parameter | E_In_Out_Parameter
               and then Present (Init_Proc_Level_Formal (Scope (E)))
            then
               return New_Occurrence_Of
                        (Init_Proc_Level_Formal (Scope (E)), Loc);

            --  Normal object - get the level of the enclosing scope

            else
               return Make_Level_Literal
                        (Scope_Depth (Enclosing_Dynamic_Scope (E)));
            end if;

         --  Handle indexed and selected components including the special cases
         --  whereby there is an implicit dereference, a component of a
         --  composite type, or a function call in prefix notation.

         --  We don't handle function calls in prefix notation correctly ???

         when N_Indexed_Component | N_Selected_Component | N_Slice =>
            Pre := Prefix (E);

            --  Fetch the original node when the prefix comes from the result
            --  of expanding a function call since we want to find the level
            --  of the original source call.

            if not Comes_From_Source (Pre)
              and then Nkind (Original_Node (Pre)) = N_Function_Call
            then
               Pre := Original_Node (Pre);
            end if;

            --  When E is an indexed component or selected component and
            --  the current Expr is a function call, we know that we are
            --  looking at an expanded call in prefix notation.

            if Nkind (Expr) = N_Function_Call then
               return Function_Call_Or_Allocator_Level (Expr);

            --  If the prefix is a named access type, then we are dealing
            --  with an implicit deferences. In that case the level is that
            --  of the named access type in the prefix.

            elsif Is_Named_Access_Type (Etype (Pre)) then
               return Make_Level_Literal
                        (Typ_Access_Level (Etype (Pre)));

            --  The current expression is a named access type, so there is no
            --  reason to look at the prefix. Instead obtain the level of E's
            --  named access type.

            elsif Is_Named_Access_Type (Etype (E)) then
               return Make_Level_Literal
                        (Typ_Access_Level (Etype (E)));

            --  A nondiscriminant selected component where the component
            --  is an anonymous access type means that its associated
            --  level is that of the containing type - see RM 3.10.2 (16).

            --  Note that when restriction No_Dynamic_Accessibility_Checks is
            --  in effect we treat discriminant components as regular
            --  components.

            elsif
              (Nkind (E) = N_Selected_Component
                and then Ekind (Etype (E))   =  E_Anonymous_Access_Type
                and then Ekind (Etype (Pre)) /= E_Anonymous_Access_Type
                and then (not (Nkind (Selector_Name (E)) in N_Has_Entity
                                and then Ekind (Entity (Selector_Name (E)))
                                           = E_Discriminant)

                           --  The alternative accessibility models both treat
                           --  discriminants as regular components.

                           or else (No_Dynamic_Accessibility_Checks_Enabled (E)
                                     and then Allow_Alt_Model)))

              --  Arrays featuring components of anonymous access components
              --  get their corresponding level from their containing type's
              --  declaration.

              or else
                (Nkind (E) = N_Indexed_Component
                  and then Ekind (Etype (E)) = E_Anonymous_Access_Type
                  and then Ekind (Etype (Pre)) in Array_Kind
                  and then Ekind (Component_Type (Base_Type (Etype (Pre))))
                             = E_Anonymous_Access_Type)
            then
               --  When restriction No_Dynamic_Accessibility_Checks is active
               --  and -gnatd_b set, the level is that of the designated type.

               if Allow_Alt_Model
                 and then No_Dynamic_Accessibility_Checks_Enabled (E)
                 and then Debug_Flag_Underscore_B
               then
                  return Make_Level_Literal
                           (Typ_Access_Level (Etype (E)));
               end if;

               --  Otherwise proceed normally

               return Make_Level_Literal
                        (Typ_Access_Level (Etype (Prefix (E))));

            --  The accessibility calculation routine that handles function
            --  calls (Function_Call_Level) assumes, in the case the
            --  result is of an anonymous access type, that the result will be
            --  used "in its entirety" when the call is present within an
            --  assignment or object declaration.

            --  To properly handle cases where the result is not used in its
            --  entirety, we test if the prefix of the component in question is
            --  a function call, which tells us that one of its components has
            --  been identified and is being accessed. Therefore we can
            --  conclude that the result is not used "in its entirety"
            --  according to RM 3.10.2 (10.2/3).

            elsif Nkind (Pre) = N_Function_Call
              and then not Is_Named_Access_Type (Etype (Pre))
            then
               --  Dynamic checks are generated when we are within a return
               --  value or we are in a function call within an anonymous
               --  access discriminant constraint of a return object (signified
               --  by In_Return_Context) on the side of the callee.

               --  So, in this case, return a library accessibility level to
               --  null out the check on the side of the caller.

               if (In_Return_Value (E)
                    or else In_Return_Context)
                 and then Level /= Dynamic_Level
               then
                  return Make_Level_Literal
                           (Scope_Depth (Standard_Standard));
               end if;

               return Make_Level_Literal
                        (Innermost_Master_Scope_Depth (Expr));

            --  Otherwise, continue recursing over the expression prefixes

            else
               return Accessibility_Level (Prefix (E));
            end if;

         --  Qualified expressions

         when N_Qualified_Expression =>
            if Is_Named_Access_Type (Etype (E)) then
               return Make_Level_Literal
                        (Typ_Access_Level (Etype (E)));
            else
               return Accessibility_Level (Expression (E));
            end if;

         --  Handle function calls

         when N_Function_Call =>
            return Function_Call_Or_Allocator_Level (E);

         --  Explicit dereference accessibility level calculation

         when N_Explicit_Dereference =>
            Pre := Original_Node (Prefix (E));

            --  The prefix is a named access type so the level is taken from
            --  its type.

            if Is_Named_Access_Type (Etype (Pre)) then
               return Make_Level_Literal (Typ_Access_Level (Etype (Pre)));

            --  Otherwise, recurse deeper

            else
               return Accessibility_Level (Prefix (E));
            end if;

         --  Type conversions

         when N_Type_Conversion | N_Unchecked_Type_Conversion =>
            --  View conversions are special in that they require use to
            --  inspect the expression of the type conversion.

            --  Allocators of anonymous access types are internally generated,
            --  so recurse deeper in that case as well.

            if Is_View_Conversion (E)
              or else Ekind (Etype (E)) = E_Anonymous_Access_Type
            then
               return Accessibility_Level (Expression (E));

            --  We don't care about the master if we are looking at a named
            --  access type.

            elsif Is_Named_Access_Type (Etype (E)) then
               return Make_Level_Literal
                        (Typ_Access_Level (Etype (E)));

            --  In section RM 3.10.2 (10/4) the accessibility rules for
            --  aggregates and value conversions are outlined. Are these
            --  followed in the case of initialization of an object ???

            --  Should use Innermost_Master_Scope_Depth ???

            else
               return Accessibility_Level (Current_Scope);
            end if;

         --  Default to the type accessibility level for the type of the
         --  expression's entity.

         when others =>
            return Make_Level_Literal (Typ_Access_Level (Etype (E)));
      end case;
   end Accessibility_Level;

   -------------------------------
   -- Apply_Accessibility_Check --
   -------------------------------

   procedure Apply_Accessibility_Check
     (N           : Node_Id;
      Typ         : Entity_Id;
      Insert_Node : Node_Id)
   is
      Loc : constant Source_Ptr := Sloc (N);

      Check_Cond  : Node_Id;
      Param_Ent   : Entity_Id := Param_Entity (N);
      Param_Level : Node_Id;
      Type_Level  : Node_Id;

   begin
      --  Verify we haven't tried to add a dynamic accessibility check when we
      --  shouldn't.

      pragma Assert (not No_Dynamic_Accessibility_Checks_Enabled (N));

      if Ada_Version >= Ada_2012
         and then No (Param_Ent)
         and then Is_Entity_Name (N)
         and then Ekind (Entity (N)) in E_Constant | E_Variable
         and then Present (Effective_Extra_Accessibility (Entity (N)))
      then
         Param_Ent := Entity (N);
         while Present (Renamed_Object (Param_Ent)) loop
            --  Renamed_Object must return an Entity_Name here
            --  because of preceding "Present (E_E_A (...))" test.

            Param_Ent := Entity (Renamed_Object (Param_Ent));
         end loop;
      end if;

      if Inside_A_Generic then
         return;

      --  Only apply the run-time check if the access parameter has an
      --  associated extra access level parameter and when accessibility checks
      --  are enabled.

      elsif Present (Param_Ent)
         and then Present (Get_Dynamic_Accessibility (Param_Ent))
         and then not Accessibility_Checks_Suppressed (Param_Ent)
         and then not Accessibility_Checks_Suppressed (Typ)
      then
         --  Obtain the parameter's accessibility level

         Param_Level :=
           New_Occurrence_Of (Get_Dynamic_Accessibility (Param_Ent), Loc);

         --  Use the dynamic accessibility parameter for the function's result
         --  when one has been created instead of statically referring to the
         --  deepest type level so as to appropriatly handle the rules for
         --  RM 3.10.2 (10.1/3).

         if Ekind (Scope (Param_Ent)) = E_Function
           and then In_Return_Value (N)
           and then Ekind (Typ) = E_Anonymous_Access_Type
         then
            --  Associate the level of the result type to the extra result
            --  accessibility parameter belonging to the current function.

            if Present (Extra_Accessibility_Of_Result (Scope (Param_Ent))) then
               Type_Level :=
                 New_Occurrence_Of
                   (Extra_Accessibility_Of_Result (Scope (Param_Ent)), Loc);

            --  In Ada 2005 and earlier modes, a result extra accessibility
            --  parameter is not generated and no dynamic check is performed.

            else
               return;
            end if;

         --  Otherwise get the type's accessibility level normally

         else
            Type_Level :=
              Make_Integer_Literal (Loc, Deepest_Type_Access_Level (Typ));
         end if;

         --  Raise Program_Error if the accessibility level of the access
         --  parameter is deeper than the level of the target access type.

         Check_Cond :=
           Make_Op_Gt (Loc,
             Left_Opnd  => Param_Level,
             Right_Opnd => Type_Level);

         Insert_Action (Insert_Node,
           Make_Raise_Program_Error (Loc,
             Condition => Check_Cond,
             Reason    => PE_Accessibility_Check_Failed));

         Analyze_And_Resolve (N);

         --  If constant folding has happened on the condition for the
         --  generated error, then warn about it being unconditional.

         if Nkind (Check_Cond) = N_Identifier
           and then Entity (Check_Cond) = Standard_True
         then
            Error_Msg_Warn := SPARK_Mode /= On;
            Error_Msg_N ("accessibility check fails<<", N);
            Error_Msg_N ("\Program_Error [<<", N);
         end if;
      end if;
   end Apply_Accessibility_Check;

   ---------------------------------------------
   -- Apply_Accessibility_Check_For_Allocator --
   ---------------------------------------------

   procedure Apply_Accessibility_Check_For_Allocator
     (N              : Node_Id;
      Exp            : Node_Id;
      Ref            : Node_Id;
      Built_In_Place : Boolean := False)
   is
      Loc       : constant Source_Ptr := Sloc (N);
      PtrT      : constant Entity_Id  := Etype (N);
      DesigT    : constant Entity_Id  := Designated_Type (PtrT);
      Pool_Id   : constant Entity_Id  := Associated_Storage_Pool (PtrT);
      Cond      : Node_Id;
      Fin_Call  : Node_Id;
      Free_Stmt : Node_Id;
      Obj_Ref   : Node_Id;
      Stmts     : List_Id;

   begin
      if Ada_Version >= Ada_2005
        and then Is_Class_Wide_Type (DesigT)
        and then Tagged_Type_Expansion
        and then not Scope_Suppress.Suppress (Accessibility_Check)
        and then not No_Dynamic_Accessibility_Checks_Enabled (Ref)
        and then
          (Type_Access_Level (Etype (Exp)) > Type_Access_Level (PtrT)
            or else
              (Is_Class_Wide_Type (Etype (Exp))
                and then Scope (PtrT) /= Current_Scope))
      then
         --  If the allocator was built in place, Ref is already a reference
         --  to the access object initialized to the result of the allocator
         --  (see Exp_Ch6.Make_Build_In_Place_Call_In_Allocator). We call
         --  Remove_Side_Effects for cases where the build-in-place call may
         --  still be the prefix of the reference (to avoid generating
         --  duplicate calls). Otherwise, it is the entity associated with
         --  the object containing the address of the allocated object.

         if Built_In_Place then
            Remove_Side_Effects (Ref);
            Obj_Ref := New_Copy_Tree (Ref);
         else
            Obj_Ref := New_Occurrence_Of (Ref, Loc);
         end if;

         --  For access to interface types we must generate code to displace
         --  the pointer to the base of the object since the subsequent code
         --  references components located in the TSD of the object (which
         --  is associated with the primary dispatch table --see a-tags.ads)
         --  and also generates code invoking Free, which requires also a
         --  reference to the base of the unallocated object.

         if Is_Interface (DesigT) and then Tagged_Type_Expansion then
            Obj_Ref :=
              Unchecked_Convert_To (Etype (Obj_Ref),
                Make_Function_Call (Loc,
                  Name                   =>
                    New_Occurrence_Of (RTE (RE_Base_Address), Loc),
                  Parameter_Associations => New_List (
                    Unchecked_Convert_To (RTE (RE_Address),
                      New_Copy_Tree (Obj_Ref)))));
         end if;

         --  Step 1: Create the object clean up code

         Stmts := New_List;

         --  Deallocate the object if the accessibility check fails. This is
         --  done only on targets or profiles that support deallocation.

         --    Free (Obj_Ref);

         if RTE_Available (RE_Free) then
            Free_Stmt := Make_Free_Statement (Loc, New_Copy_Tree (Obj_Ref));
            Set_Storage_Pool (Free_Stmt, Pool_Id);

            Append_To (Stmts, Free_Stmt);

         --  The target or profile cannot deallocate objects

         else
            Free_Stmt := Empty;
         end if;

         --  Finalize the object if applicable. Generate:

         --    [Deep_]Finalize (Obj_Ref.all);

         if Needs_Finalization (DesigT)
           and then not No_Heap_Finalization (PtrT)
         then
            Fin_Call :=
              Make_Final_Call
                (Obj_Ref =>
                   Make_Explicit_Dereference (Loc, New_Copy (Obj_Ref)),
                 Typ     => DesigT);

            --  Guard against a missing [Deep_]Finalize when the designated
            --  type was not properly frozen.

            if No (Fin_Call) then
               Fin_Call := Make_Null_Statement (Loc);
            end if;

            --  When the target or profile supports deallocation, wrap the
            --  finalization call in a block to ensure proper deallocation even
            --  if finalization fails. Generate:

            --    begin
            --       <Fin_Call>
            --    exception
            --       when others =>
            --          <Free_Stmt>
            --          raise;
            --    end;

            if Present (Free_Stmt) then
               Fin_Call :=
                 Make_Block_Statement (Loc,
                   Handled_Statement_Sequence =>
                     Make_Handled_Sequence_Of_Statements (Loc,
                       Statements => New_List (Fin_Call),

                     Exception_Handlers => New_List (
                       Make_Exception_Handler (Loc,
                         Exception_Choices => New_List (
                           Make_Others_Choice (Loc)),
                         Statements        => New_List (
                           New_Copy_Tree (Free_Stmt),
                           Make_Raise_Statement (Loc))))));
            end if;

            Prepend_To (Stmts, Fin_Call);
         end if;

         --  Signal the accessibility failure through a Program_Error

         Append_To (Stmts,
           Make_Raise_Program_Error (Loc,
             Reason => PE_Accessibility_Check_Failed));

         --  Step 2: Create the accessibility comparison

         --  Generate:
         --    Ref'Tag

         Obj_Ref :=
           Make_Attribute_Reference (Loc,
             Prefix         => Obj_Ref,
             Attribute_Name => Name_Tag);

         --  For tagged types, determine the accessibility level by looking at
         --  the type specific data of the dispatch table. Generate:

         --    Type_Specific_Data (Address (Ref'Tag)).Access_Level

         if Tagged_Type_Expansion then
            Cond := Build_Get_Access_Level (Loc, Obj_Ref);

         --  Use a runtime call to determine the accessibility level when
         --  compiling on virtual machine targets. Generate:

         --    Get_Access_Level (Ref'Tag)

         else
            Cond :=
              Make_Function_Call (Loc,
                Name                   =>
                  New_Occurrence_Of (RTE (RE_Get_Access_Level), Loc),
                Parameter_Associations => New_List (Obj_Ref));
         end if;

         Cond :=
           Make_Op_Gt (Loc,
             Left_Opnd  => Cond,
             Right_Opnd => Accessibility_Level (N, Dynamic_Level));

         --  Due to the complexity and side effects of the check, utilize an if
         --  statement instead of the regular Program_Error circuitry.

         Insert_Action (N,
           Make_Implicit_If_Statement (N,
             Condition       => Cond,
             Then_Statements => Stmts));
      end if;
   end Apply_Accessibility_Check_For_Allocator;

   ------------------------------------------
   -- Check_Return_Construct_Accessibility --
   ------------------------------------------

   procedure Check_Return_Construct_Accessibility
     (Return_Stmt : Node_Id;
      Stm_Entity  : Entity_Id)
   is
      Loc      : constant Source_Ptr := Sloc (Return_Stmt);
      Scope_Id : constant Entity_Id  := Return_Applies_To (Stm_Entity);

      R_Type : constant Entity_Id := Etype (Scope_Id);
      --  Function result subtype

      function First_Selector (Assoc : Node_Id) return Node_Id;
      --  Obtain the first selector or choice from a given association

      function Is_Formal_Of_Current_Function
        (Assoc_Expr : Entity_Id) return Boolean;
      --  Predicate to test if a given expression associated with a
      --  discriminant is a formal parameter to the function in which the
      --  return construct we checking applies to.

      --------------------
      -- First_Selector --
      --------------------

      function First_Selector (Assoc : Node_Id) return Node_Id is
      begin
         if Nkind (Assoc) = N_Component_Association then
            return First (Choices (Assoc));

         elsif Nkind (Assoc) = N_Discriminant_Association then
            return (First (Selector_Names (Assoc)));

         else
            raise Program_Error;
         end if;
      end First_Selector;

      -----------------------------------
      -- Is_Formal_Of_Current_Function --
      -----------------------------------

      function Is_Formal_Of_Current_Function
        (Assoc_Expr : Entity_Id) return Boolean is
      begin
         return Is_Entity_Name (Assoc_Expr)
                  and then Enclosing_Subprogram
                             (Entity (Assoc_Expr)) = Scope_Id
                  and then Is_Formal (Entity (Assoc_Expr));
      end Is_Formal_Of_Current_Function;

      --  Local declarations

      Assoc : Node_Id := Empty;
      --  Assoc should perhaps be renamed and declared as a
      --  Node_Or_Entity_Id since it encompasses not only component and
      --  discriminant associations, but also discriminant components within
      --  a type declaration or subtype indication ???

      Assoc_Expr    : Node_Id;
      Assoc_Present : Boolean := False;

      Check_Cond        : Node_Id;
      Unseen_Disc_Count : Nat := 0;
      Seen_Discs        : Elist_Id;
      Disc              : Entity_Id;
      First_Disc        : Entity_Id;

      Obj_Decl   : Node_Id;
      Return_Con : Node_Id;
      Unqual     : Node_Id;

   --  Start of processing for Check_Return_Construct_Accessibility

   begin
      --  Only perform checks on record types with access discriminants and
      --  non-internally generated functions.

      if not Is_Record_Type (R_Type)
        or else not Has_Anonymous_Access_Discriminant (R_Type)
        or else not Comes_From_Source (Return_Stmt)
      then
         return;
      end if;

      --  We are only interested in return statements

      if Nkind (Return_Stmt) not in
           N_Extended_Return_Statement | N_Simple_Return_Statement
      then
         return;
      end if;

      --  Fetch the object from the return statement, in the case of a
      --  simple return statement the expression is part of the node.

      if Nkind (Return_Stmt) = N_Extended_Return_Statement then
         --  Obtain the object definition from the expanded extended return

         Return_Con := First (Return_Object_Declarations (Return_Stmt));
         while Present (Return_Con) loop
            --  Inspect the original node to avoid object declarations
            --  expanded into renamings.

            if Nkind (Original_Node (Return_Con)) = N_Object_Declaration
              and then Comes_From_Source (Original_Node (Return_Con))
            then
               exit;
            end if;

            Nlists.Next (Return_Con);
         end loop;

         pragma Assert (Present (Return_Con));

         --  Could be dealing with a renaming

         Return_Con := Original_Node (Return_Con);
      else
         Return_Con := Expression (Return_Stmt);
      end if;

      --  Obtain the accessibility levels of the expressions associated
      --  with all anonymous access discriminants, then generate a
      --  dynamic check or static error when relevant.

      --  Note the repeated use of Original_Node to avoid checking
      --  expanded code.

      Unqual := Original_Node (Unqualify (Original_Node (Return_Con)));

      --  Get the corresponding declaration based on the return object's
      --  identifier.

      if Nkind (Unqual) = N_Identifier
        and then Nkind (Parent (Entity (Unqual)))
                   in N_Object_Declaration
                    | N_Object_Renaming_Declaration
      then
         Obj_Decl := Original_Node (Parent (Entity (Unqual)));

      --  We were passed the object declaration directly, so use it

      elsif Nkind (Unqual) in N_Object_Declaration
                            | N_Object_Renaming_Declaration
      then
         Obj_Decl := Unqual;

      --  Otherwise, we are looking at something else

      else
         Obj_Decl := Empty;

      end if;

      --  Hop up object renamings when present

      if Present (Obj_Decl)
        and then Nkind (Obj_Decl) = N_Object_Renaming_Declaration
      then
         while Nkind (Obj_Decl) = N_Object_Renaming_Declaration loop

            if Nkind (Name (Obj_Decl)) not in N_Entity then
               --  We may be looking at the expansion of iterators or
               --  some other internally generated construct, so it is safe
               --  to ignore checks ???

               if not Comes_From_Source (Obj_Decl) then
                  return;
               end if;

               Obj_Decl := Original_Node
                             (Declaration_Node
                               (Ultimate_Prefix (Name (Obj_Decl))));

            --  Move up to the next declaration based on the object's name

            else
               Obj_Decl := Original_Node
                             (Declaration_Node (Name (Obj_Decl)));
            end if;
         end loop;
      end if;

      --  Obtain the discriminant values from the return aggregate

      --  Do we cover extension aggregates correctly ???

      if Nkind (Unqual) = N_Aggregate then
         if Present (Expressions (Unqual)) then
            Assoc := First (Expressions (Unqual));
         else
            Assoc := First (Component_Associations (Unqual));
         end if;

      --  There is an object declaration for the return object

      elsif Present (Obj_Decl) then
         --  When a subtype indication is present in an object declaration
         --  it must contain the object's discriminants.

         if Nkind (Object_Definition (Obj_Decl)) = N_Subtype_Indication then
            Assoc := First
                       (Constraints
                         (Constraint
                           (Object_Definition (Obj_Decl))));

         --  The object declaration contains an aggregate

         elsif Present (Expression (Obj_Decl)) then

            if Nkind (Unqualify (Expression (Obj_Decl))) = N_Aggregate then
               --  Grab the first associated discriminant expresion

               if Present
                    (Expressions (Unqualify (Expression (Obj_Decl))))
               then
                  Assoc := First
                             (Expressions
                               (Unqualify (Expression (Obj_Decl))));
               else
                  Assoc := First
                             (Component_Associations
                               (Unqualify (Expression (Obj_Decl))));
               end if;

            --  Otherwise, this is something else

            else
               return;
            end if;

         --  There are no supplied discriminants in the object declaration,
         --  so get them from the type definition since they must be default
         --  initialized.

         --  Do we handle constrained subtypes correctly ???

         elsif Nkind (Unqual) = N_Object_Declaration then
            Assoc := First_Discriminant
                       (Etype (Object_Definition (Obj_Decl)));

         else
            Assoc := First_Discriminant (Etype (Unqual));
         end if;

      --  When we are not looking at an aggregate or an identifier, return
      --  since any other construct (like a function call) is not
      --  applicable since checks will be performed on the side of the
      --  callee.

      else
         return;
      end if;

      --  Obtain the discriminants so we know the actual type in case the
      --  value of their associated expression gets implicitly converted.

      if No (Obj_Decl) then
         pragma Assert (Nkind (Unqual) = N_Aggregate);

         Disc := First_Discriminant (Etype (Unqual));

      else
         Disc := First_Discriminant
                   (Etype (Defining_Identifier (Obj_Decl)));
      end if;

      --  Preserve the first discriminant for checking named associations

      First_Disc := Disc;

      --  Count the number of discriminants for processing an aggregate
      --  which includes an others.

      Disc := First_Disc;
      while Present (Disc) loop
         Unseen_Disc_Count := Unseen_Disc_Count + 1;

         Next_Discriminant (Disc);
      end loop;

      Seen_Discs := New_Elmt_List;

      --  Loop through each of the discriminants and check each expression
      --  associated with an anonymous access discriminant.

      --  When named associations occur in the return aggregate then
      --  discriminants can be in any order, so we need to ensure we do
      --  not continue to loop when all discriminants have been seen.

      Disc := First_Disc;
      while Present (Assoc)
        and then (Present (Disc) or else Assoc_Present)
        and then Unseen_Disc_Count > 0
      loop
         --  Handle named associations by searching through the names of
         --  the relevant discriminant components.

         if Nkind (Assoc)
              in N_Component_Association | N_Discriminant_Association
         then
            Assoc_Expr    := Expression (Assoc);
            Assoc_Present := True;

            --  We currently don't handle box initialized discriminants,
            --  however, since default initialized anonymous access
            --  discriminants are a corner case, this is ok for now ???

            if Nkind (Assoc) = N_Component_Association
              and then Box_Present (Assoc)
            then
               if Nkind (First_Selector (Assoc)) = N_Others_Choice then
                  Unseen_Disc_Count := 0;
               end if;

            --  When others is present we must identify a discriminant we
            --  haven't already seen so as to get the appropriate type for
            --  the static accessibility check.

            --  This works because all components within an others clause
            --  must have the same type.

            elsif Nkind (First_Selector (Assoc)) = N_Others_Choice then

               Disc := First_Disc;
               Outer : while Present (Disc) loop
                  declare
                     Current_Seen_Disc : Elmt_Id;
                  begin
                     --  Move through the list of identified discriminants

                     Current_Seen_Disc := First_Elmt (Seen_Discs);
                     while Present (Current_Seen_Disc) loop
                        --  Exit the loop when we found a match

                        exit when
                          Chars (Node (Current_Seen_Disc)) = Chars (Disc);

                        Next_Elmt (Current_Seen_Disc);
                     end loop;

                     --  When we have exited the above loop without finding
                     --  a match then we know that Disc has not been seen.

                     exit Outer when No (Current_Seen_Disc);
                  end;

                  Next_Discriminant (Disc);
               end loop Outer;

               --  If we got to an others clause with a non-zero
               --  discriminant count there must be a discriminant left to
               --  check.

               pragma Assert (Present (Disc));

               --  Set the unseen discriminant count to zero because we know
               --  an others clause sets all remaining components of an
               --  aggregate.

               Unseen_Disc_Count := 0;

            --  Move through each of the selectors in the named association
            --  and obtain a discriminant for accessibility checking if one
            --  is referenced in the list. Also track which discriminants
            --  are referenced for the purpose of handling an others clause.

            else
               declare
                  Assoc_Choice : Node_Id;
                  Curr_Disc    : Node_Id;
               begin

                  Disc      := Empty;
                  Curr_Disc := First_Disc;
                  while Present (Curr_Disc) loop
                     --  Check each of the choices in the associations for a
                     --  match to the name of the current discriminant.

                     Assoc_Choice := First_Selector (Assoc);
                     while Present (Assoc_Choice) loop
                        --  When the name matches we track that we have seen
                        --  the discriminant, but instead of exiting the
                        --  loop we continue iterating to make sure all the
                        --  discriminants within the named association get
                        --  tracked.

                        if Chars (Assoc_Choice) = Chars (Curr_Disc) then
                           Append_Elmt (Curr_Disc, Seen_Discs);

                           Disc              := Curr_Disc;
                           Unseen_Disc_Count := Unseen_Disc_Count - 1;
                        end if;

                        Next (Assoc_Choice);
                     end loop;

                     Next_Discriminant (Curr_Disc);
                  end loop;
               end;
            end if;

         --  Unwrap the associated expression if we are looking at a default
         --  initialized type declaration. In this case Assoc is not really
         --  an association, but a component declaration. Should Assoc be
         --  renamed in some way to be more clear ???

         --  This occurs when the return object does not initialize
         --  discriminant and instead relies on the type declaration for
         --  their supplied values.

         elsif Nkind (Assoc) in N_Entity
           and then Ekind (Assoc) = E_Discriminant
         then
            Append_Elmt (Disc, Seen_Discs);

            Assoc_Expr        := Discriminant_Default_Value (Assoc);
            Unseen_Disc_Count := Unseen_Disc_Count - 1;

         --  Otherwise, there is nothing to do because Assoc is an
         --  expression within the return aggregate itself.

         else
            Append_Elmt (Disc, Seen_Discs);

            Assoc_Expr        := Assoc;
            Unseen_Disc_Count := Unseen_Disc_Count - 1;
         end if;

         --  Check the accessibility level of the expression when the
         --  discriminant is of an anonymous access type.

         if Present (Assoc_Expr)
           and then Present (Disc)
           and then Ekind (Etype (Disc)) = E_Anonymous_Access_Type

           --  We disable the check when we have a tagged return type and
           --  the associated expression for the discriminant is a formal
           --  parameter since the check would require us to compare the
           --  accessibility level of Assoc_Expr to the level of the
           --  Extra_Accessibility_Of_Result of the function - which is
           --  currently disabled for functions with tagged return types.
           --  This may change in the future ???

           --  See Needs_Result_Accessibility_Level for details.

           and then not
             (No (Extra_Accessibility_Of_Result (Scope_Id))
               and then Is_Formal_Of_Current_Function (Assoc_Expr)
               and then Is_Tagged_Type (Etype (Scope_Id)))
         then
            --  Generate a dynamic check based on the extra accessibility of
            --  the result or the scope of the current function.

            Check_Cond :=
              Make_Op_Gt (Loc,
                Left_Opnd  => Accessibility_Level
                                (Expr              => Assoc_Expr,
                                 Level             => Dynamic_Level,
                                 In_Return_Context => True),
                Right_Opnd =>
                  (if Present (Extra_Accessibility_Of_Result (Scope_Id))

                     --  When Assoc_Expr is a formal we have to look at the
                     --  extra accessibility-level formal associated with
                     --  the result.

                     and then Is_Formal_Of_Current_Function (Assoc_Expr)
                   then
                      New_Occurrence_Of
                        (Extra_Accessibility_Of_Result (Scope_Id), Loc)

                   --  Otherwise, we compare the level of Assoc_Expr to the
                   --  scope of the current function.

                   else
                      Make_Integer_Literal
                        (Loc, Scope_Depth (Scope (Scope_Id)))));

            Insert_Before_And_Analyze (Return_Stmt,
              Make_Raise_Program_Error (Loc,
                Condition => Check_Cond,
                Reason    => PE_Accessibility_Check_Failed));

            --  If constant folding has happened on the condition for the
            --  generated error, then warn about it being unconditional when
            --  we know an error will be raised.

            if Nkind (Check_Cond) = N_Identifier
              and then Entity (Check_Cond) = Standard_True
            then
               Error_Msg_N
                 ("access discriminant in return object would be a dangling"
                  & " reference", Return_Stmt);
            end if;
         end if;

         --  Iterate over the discriminants, except when we have encountered
         --  a named association since the discriminant order becomes
         --  irrelevant in that case.

         if not Assoc_Present then
            Next_Discriminant (Disc);
         end if;

         --  Iterate over associations

         if not Is_List_Member (Assoc) then
            exit;
         else
            Nlists.Next (Assoc);
         end if;
      end loop;
   end Check_Return_Construct_Accessibility;

   -------------------------------
   -- Deepest_Type_Access_Level --
   -------------------------------

   function Deepest_Type_Access_Level
     (Typ             : Entity_Id;
      Allow_Alt_Model : Boolean := True) return Uint
   is
   begin
      if Ekind (Typ) = E_Anonymous_Access_Type
        and then not Is_Local_Anonymous_Access (Typ)
        and then Nkind (Associated_Node_For_Itype (Typ)) = N_Object_Declaration
      then
         --  No_Dynamic_Accessibility_Checks override for alternative
         --  accessibility model.

         if Allow_Alt_Model
           and then No_Dynamic_Accessibility_Checks_Enabled (Typ)
         then
            return Type_Access_Level (Typ, Allow_Alt_Model);
         end if;

         --  Typ is the type of an Ada 2012 stand-alone object of an anonymous
         --  access type.

         return
           Scope_Depth (Enclosing_Dynamic_Scope
                         (Defining_Identifier
                           (Associated_Node_For_Itype (Typ))));

      --  For generic formal type, return Int'Last (infinite).
      --  See comment preceding Is_Generic_Type call in Type_Access_Level.

      elsif Is_Generic_Type (Root_Type (Typ)) then
         return UI_From_Int (Int'Last);

      else
         return Type_Access_Level (Typ, Allow_Alt_Model);
      end if;
   end Deepest_Type_Access_Level;

   -----------------------------------
   -- Effective_Extra_Accessibility --
   -----------------------------------

   function Effective_Extra_Accessibility (Id : Entity_Id) return Entity_Id is
   begin
      if Present (Renamed_Object (Id))
        and then Is_Entity_Name (Renamed_Object (Id))
      then
         return Effective_Extra_Accessibility (Entity (Renamed_Object (Id)));
      else
         return Extra_Accessibility (Id);
      end if;
   end Effective_Extra_Accessibility;

   -------------------------------
   -- Get_Dynamic_Accessibility --
   -------------------------------

   function Get_Dynamic_Accessibility (E : Entity_Id) return Entity_Id is
   begin
      --  When minimum accessibility is set for E then we utilize it - except
      --  in a few edge cases like the expansion of select statements where
      --  generated subprogram may attempt to unnecessarily use a minimum
      --  accessibility object declared outside of scope.

      --  To avoid these situations where expansion may get complex we verify
      --  that the minimum accessibility object is within scope.

      if Is_Formal (E)
        and then Present (Minimum_Accessibility (E))
        and then In_Open_Scopes (Scope (Minimum_Accessibility (E)))
      then
         return Minimum_Accessibility (E);
      end if;

      return Extra_Accessibility (E);
   end Get_Dynamic_Accessibility;

   -----------------------
   -- Has_Access_Values --
   -----------------------

   function Has_Access_Values (T : Entity_Id) return Boolean
   is
      Typ : constant Entity_Id := Underlying_Type (T);

   begin
      --  Case of a private type which is not completed yet. This can only
      --  happen in the case of a generic formal type appearing directly, or
      --  as a component of the type to which this function is being applied
      --  at the top level. Return False in this case, since we certainly do
      --  not know that the type contains access types.

      if No (Typ) then
         return False;

      elsif Is_Access_Type (Typ) then
         return True;

      elsif Is_Array_Type (Typ) then
         return Has_Access_Values (Component_Type (Typ));

      elsif Is_Record_Type (Typ) then
         declare
            Comp : Entity_Id;

         begin
            --  Loop to check components

            Comp := First_Component_Or_Discriminant (Typ);
            while Present (Comp) loop

               --  Check for access component, tag field does not count, even
               --  though it is implemented internally using an access type.

               if Has_Access_Values (Etype (Comp))
                 and then Chars (Comp) /= Name_uTag
               then
                  return True;
               end if;

               Next_Component_Or_Discriminant (Comp);
            end loop;
         end;

         return False;

      else
         return False;
      end if;
   end Has_Access_Values;

   ---------------------------------------
   -- Has_Anonymous_Access_Discriminant --
   ---------------------------------------

   function Has_Anonymous_Access_Discriminant (Typ : Entity_Id) return Boolean
   is
      Disc : Node_Id;

   begin
      if not Has_Discriminants (Typ) then
         return False;
      end if;

      Disc := First_Discriminant (Typ);
      while Present (Disc) loop
         if Ekind (Etype (Disc)) = E_Anonymous_Access_Type then
            return True;
         end if;

         Next_Discriminant (Disc);
      end loop;

      return False;
   end Has_Anonymous_Access_Discriminant;

   --------------------------------------------
   -- Has_Unconstrained_Access_Discriminants --
   --------------------------------------------

   function Has_Unconstrained_Access_Discriminants
     (Subtyp : Entity_Id) return Boolean
   is
      Discr : Entity_Id;

   begin
      if Has_Discriminants (Subtyp)
        and then not Is_Constrained (Subtyp)
      then
         Discr := First_Discriminant (Subtyp);
         while Present (Discr) loop
            if Ekind (Etype (Discr)) = E_Anonymous_Access_Type then
               return True;
            end if;

            Next_Discriminant (Discr);
         end loop;
      end if;

      return False;
   end Has_Unconstrained_Access_Discriminants;

   --------------------------------
   -- Is_Anonymous_Access_Actual --
   --------------------------------

   function Is_Anonymous_Access_Actual (N : Node_Id) return Boolean is
      Par : Node_Id;
   begin
      if Ekind (Etype (N)) /= E_Anonymous_Access_Type then
         return False;
      end if;

      Par := Parent (N);
      while Present (Par)
        and then Nkind (Par) in N_Case_Expression
                              | N_If_Expression
                              | N_Parameter_Association
      loop
         Par := Parent (Par);
      end loop;
      return Nkind (Par) in N_Subprogram_Call;
   end Is_Anonymous_Access_Actual;

   --------------------------------------
   -- Is_Special_Aliased_Formal_Access --
   --------------------------------------

   function Is_Special_Aliased_Formal_Access
     (Exp               : Node_Id;
      In_Return_Context : Boolean := False) return Boolean
   is
      Scop : constant Entity_Id := Current_Subprogram;
   begin
      --  Verify the expression is an access reference to 'Access within a
      --  return statement as this is the only time an explicitly aliased
      --  formal has different semantics.

      if Nkind (Exp) /= N_Attribute_Reference
        or else Get_Attribute_Id (Attribute_Name (Exp)) /= Attribute_Access
        or else not (In_Return_Value (Exp)
                      or else In_Return_Context)
        or else not Needs_Result_Accessibility_Level (Scop)
      then
         return False;
      end if;

      --  Check if the prefix of the reference is indeed an explicitly aliased
      --  formal parameter for the function Scop. Additionally, we must check
      --  that Scop returns an anonymous access type, otherwise the special
      --  rules dictating a need for a dynamic check are not in effect.

      return Is_Entity_Name (Prefix (Exp))
               and then Is_Explicitly_Aliased (Entity (Prefix (Exp)));
   end Is_Special_Aliased_Formal_Access;

   --------------------------------------
   -- Needs_Result_Accessibility_Level --
   --------------------------------------

   function Needs_Result_Accessibility_Level
     (Func_Id : Entity_Id) return Boolean
   is
      Func_Typ : constant Entity_Id := Underlying_Type (Etype (Func_Id));

      function Has_Unconstrained_Access_Discriminant_Component
        (Comp_Typ : Entity_Id) return Boolean;
      --  Returns True if any component of the type has an unconstrained access
      --  discriminant.

      -----------------------------------------------------
      -- Has_Unconstrained_Access_Discriminant_Component --
      -----------------------------------------------------

      function Has_Unconstrained_Access_Discriminant_Component
        (Comp_Typ :  Entity_Id) return Boolean
      is
      begin
         if not Is_Limited_Type (Comp_Typ) then
            return False;

         --  Only limited types can have access discriminants with
         --  defaults.

         elsif Has_Unconstrained_Access_Discriminants (Comp_Typ) then
            return True;

         elsif Is_Array_Type (Comp_Typ) then
            return Has_Unconstrained_Access_Discriminant_Component
                     (Underlying_Type (Component_Type (Comp_Typ)));

         elsif Is_Record_Type (Comp_Typ) then
            declare
               Comp : Entity_Id;

            begin
               Comp := First_Component (Comp_Typ);
               while Present (Comp) loop
                  if Has_Unconstrained_Access_Discriminant_Component
                       (Underlying_Type (Etype (Comp)))
                  then
                     return True;
                  end if;

                  Next_Component (Comp);
               end loop;
            end;
         end if;

         return False;
      end Has_Unconstrained_Access_Discriminant_Component;

      Disable_Tagged_Cases : constant Boolean := True;
      --  Flag used to temporarily disable a "True" result for tagged types.
      --  See comments further below for details.

   --  Start of processing for Needs_Result_Accessibility_Level

   begin
      --  False if completion unavailable, which can happen when we are
      --  analyzing an abstract subprogram or if the subprogram has
      --  delayed freezing.

      if No (Func_Typ) then
         return False;

      --  False if not a function, also handle enum-lit renames case

      elsif Func_Typ = Standard_Void_Type
        or else Is_Scalar_Type (Func_Typ)
      then
         return False;

      --  Handle a corner case, a cross-dialect subp renaming. For example,
      --  an Ada 2012 renaming of an Ada 2005 subprogram. This can occur when
      --  an Ada 2005 (or earlier) unit references predefined run-time units.

      elsif Present (Alias (Func_Id)) then

         --  Unimplemented: a cross-dialect subp renaming which does not set
         --  the Alias attribute (e.g., a rename of a dereference of an access
         --  to subprogram value). ???

         return Present (Extra_Accessibility_Of_Result (Alias (Func_Id)));

      --  Remaining cases require Ada 2012 mode, unless they are dispatching
      --  operations, since they may be overridden by Ada_2012 primitives.

      elsif Ada_Version < Ada_2012
        and then not Is_Dispatching_Operation (Func_Id)
      then
         return False;

      --  Handle the situation where a result is an anonymous access type
      --  RM 3.10.2 (10.3/3).

      elsif Ekind (Func_Typ) = E_Anonymous_Access_Type then
         return True;

      --  In the case of, say, a null tagged record result type, the need for
      --  this extra parameter might not be obvious so this function returns
      --  True for all tagged types for compatibility reasons.

      --  A function with, say, a tagged null controlling result type might
      --  be overridden by a primitive of an extension having an access
      --  discriminant and the overrider and overridden must have compatible
      --  calling conventions (including implicitly declared parameters).

      --  Similarly, values of one access-to-subprogram type might designate
      --  both a primitive subprogram of a given type and a function which is,
      --  for example, not a primitive subprogram of any type. Again, this
      --  requires calling convention compatibility. It might be possible to
      --  solve these issues by introducing wrappers, but that is not the
      --  approach that was chosen.

      --  Note: Despite the reasoning noted above, the extra accessibility
      --  parameter for tagged types is disabled for performance reasons.

      elsif Is_Tagged_Type (Func_Typ) then
         return not Disable_Tagged_Cases;

      elsif Has_Unconstrained_Access_Discriminants (Func_Typ) then
         return True;

      elsif Has_Unconstrained_Access_Discriminant_Component (Func_Typ) then
         return True;

      --  False for all other cases

      else
         return False;
      end if;
   end Needs_Result_Accessibility_Level;

   ------------------------------------------
   -- Prefix_With_Safe_Accessibility_Level --
   ------------------------------------------

   function Prefix_With_Safe_Accessibility_Level
     (N   : Node_Id;
      Typ : Entity_Id) return Boolean
   is
      P        : constant Node_Id      := Prefix (N);
      Aname    : constant Name_Id      := Attribute_Name (N);
      Attr_Id  : constant Attribute_Id := Get_Attribute_Id (Aname);
      Btyp     : constant Entity_Id    := Base_Type (Typ);

      function Safe_Value_Conversions return Boolean;
      --  Return False if the prefix has a value conversion of an array type

      ----------------------------
      -- Safe_Value_Conversions --
      ----------------------------

      function Safe_Value_Conversions return Boolean is
         PP : Node_Id := P;

      begin
         loop
            if Nkind (PP) in N_Selected_Component | N_Indexed_Component then
               PP := Prefix (PP);

            elsif Comes_From_Source (PP)
              and then Nkind (PP) in N_Type_Conversion
                                   | N_Unchecked_Type_Conversion
              and then Is_Array_Type (Etype (PP))
            then
               return False;

            elsif Comes_From_Source (PP)
              and then Nkind (PP) = N_Qualified_Expression
              and then Is_Array_Type (Etype (PP))
              and then Nkind (Original_Node (Expression (PP))) in
                         N_Aggregate | N_Extension_Aggregate
            then
               return False;

            else
               exit;
            end if;
         end loop;

         return True;
      end Safe_Value_Conversions;

   --  Start of processing for Prefix_With_Safe_Accessibility_Level

   begin
      --  No check required for unchecked and unrestricted access

      if Attr_Id = Attribute_Unchecked_Access
        or else Attr_Id = Attribute_Unrestricted_Access
      then
         return True;

      --  Check value conversions

      elsif Ekind (Btyp) = E_General_Access_Type
        and then not Safe_Value_Conversions
      then
         return False;
      end if;

      return True;
   end Prefix_With_Safe_Accessibility_Level;

   -----------------------------
   -- Subprogram_Access_Level --
   -----------------------------

   function Subprogram_Access_Level (Subp : Entity_Id) return Uint is
   begin
      if Present (Alias (Subp)) then
         return Subprogram_Access_Level (Alias (Subp));
      else
         return Scope_Depth (Enclosing_Dynamic_Scope (Subp));
      end if;
   end Subprogram_Access_Level;

   --------------------------------
   -- Static_Accessibility_Level --
   --------------------------------

   function Static_Accessibility_Level
     (Expr              : Node_Id;
      Level             : Static_Accessibility_Level_Kind;
      In_Return_Context : Boolean := False) return Uint
   is
   begin
      return Intval
               (Accessibility_Level (Expr, Level, In_Return_Context));
   end Static_Accessibility_Level;

   -----------------------
   -- Type_Access_Level --
   -----------------------

   function Type_Access_Level
     (Typ             : Entity_Id;
      Allow_Alt_Model : Boolean   := True;
      Assoc_Ent       : Entity_Id := Empty) return Uint
   is
      Btyp    : Entity_Id := Base_Type (Typ);
      Def_Ent : Entity_Id;

   begin
      --  Ada 2005 (AI-230): For most cases of anonymous access types, we
      --  simply use the level where the type is declared. This is true for
      --  stand-alone object declarations, and for anonymous access types
      --  associated with components the level is the same as that of the
      --  enclosing composite type. However, special treatment is needed for
      --  the cases of access parameters, return objects of an anonymous access
      --  type, and, in Ada 95, access discriminants of limited types.

      if Is_Access_Type (Btyp) then
         if Ekind (Btyp) = E_Anonymous_Access_Type then
            --  No_Dynamic_Accessibility_Checks restriction override for
            --  alternative accessibility model.

            if Allow_Alt_Model
              and then No_Dynamic_Accessibility_Checks_Enabled (Btyp)
            then
               --  In the -gnatd_b model, the level of an anonymous access
               --  type is always that of the designated type.

               if Debug_Flag_Underscore_B then
                  return Type_Access_Level
                           (Designated_Type (Btyp), Allow_Alt_Model);
               end if;

               --  When an anonymous access type's Assoc_Ent is specified,
               --  calculate the result based on the general accessibility
               --  level routine.

               --  We would like to use Associated_Node_For_Itype here instead,
               --  but in some cases it is not fine grained enough ???

               if Present (Assoc_Ent) then
                  return Static_Accessibility_Level
                           (Assoc_Ent, Object_Decl_Level);
               end if;

               --  Otherwise take the context of the anonymous access type into
               --  account.

               --  Obtain the defining entity for the internally generated
               --  anonymous access type.

               Def_Ent := Defining_Entity_Or_Empty
                            (Associated_Node_For_Itype (Typ));

               if Present (Def_Ent) then
                  --  When the defining entity is a subprogram then we know the
                  --  anonymous access type Typ has been generated to either
                  --  describe an anonymous access type formal or an anonymous
                  --  access result type.

                  --  Since we are only interested in the formal case, avoid
                  --  the anonymous access result type.

                  if Is_Subprogram (Def_Ent)
                    and then not (Ekind (Def_Ent) = E_Function
                                   and then Etype (Def_Ent) = Typ)
                  then
                     --  When the type comes from an anonymous access
                     --  parameter, the level is that of the subprogram
                     --  declaration.

                     return Scope_Depth (Def_Ent);

                  --  When the type is an access discriminant, the level is
                  --  that of the type.

                  elsif Ekind (Def_Ent) = E_Discriminant then
                     return Scope_Depth (Scope (Def_Ent));
                  end if;
               end if;

            --  If the type is a nonlocal anonymous access type (such as for
            --  an access parameter) we treat it as being declared at the
            --  library level to ensure that names such as X.all'access don't
            --  fail static accessibility checks.

            elsif not Is_Local_Anonymous_Access (Typ) then
               return Scope_Depth (Standard_Standard);

            --  If this is a return object, the accessibility level is that of
            --  the result subtype of the enclosing function. The test here is
            --  little complicated, because we have to account for extended
            --  return statements that have been rewritten as blocks, in which
            --  case we have to find and the Is_Return_Object attribute of the
            --  itype's associated object. It would be nice to find a way to
            --  simplify this test, but it doesn't seem worthwhile to add a new
            --  flag just for purposes of this test. ???

            elsif Ekind (Scope (Btyp)) = E_Return_Statement
              or else
                (Is_Itype (Btyp)
                  and then Nkind (Associated_Node_For_Itype (Btyp)) =
                                                         N_Object_Declaration
                  and then Is_Return_Object
                             (Defining_Identifier
                                (Associated_Node_For_Itype (Btyp))))
            then
               declare
                  Scop : Entity_Id;

               begin
                  Scop := Scope (Scope (Btyp));
                  while Present (Scop) loop
                     exit when Ekind (Scop) = E_Function;
                     Scop := Scope (Scop);
                  end loop;

                  --  Treat the return object's type as having the level of the
                  --  function's result subtype (as per RM05-6.5(5.3/2)).

                  return Type_Access_Level (Etype (Scop), Allow_Alt_Model);
               end;
            end if;
         end if;

         Btyp := Root_Type (Btyp);

         --  The accessibility level of anonymous access types associated with
         --  discriminants is that of the current instance of the type, and
         --  that's deeper than the type itself (AARM 3.10.2 (12.3.21)).

         --  AI-402: access discriminants have accessibility based on the
         --  object rather than the type in Ada 2005, so the above paragraph
         --  doesn't apply.

         --  ??? Needs completion with rules from AI-416

         if Ada_Version <= Ada_95
           and then Ekind (Typ) = E_Anonymous_Access_Type
           and then Present (Associated_Node_For_Itype (Typ))
           and then Nkind (Associated_Node_For_Itype (Typ)) =
                                                 N_Discriminant_Specification
         then
            return Scope_Depth (Enclosing_Dynamic_Scope (Btyp)) + 1;
         end if;
      end if;

      --  Return library level for a generic formal type. This is done because
      --  RM(10.3.2) says that "The statically deeper relationship does not
      --  apply to ... a descendant of a generic formal type". Rather than
      --  checking at each point where a static accessibility check is
      --  performed to see if we are dealing with a formal type, this rule is
      --  implemented by having Type_Access_Level and Deepest_Type_Access_Level
      --  return extreme values for a formal type; Deepest_Type_Access_Level
      --  returns Int'Last. By calling the appropriate function from among the
      --  two, we ensure that the static accessibility check will pass if we
      --  happen to run into a formal type. More specifically, we should call
      --  Deepest_Type_Access_Level instead of Type_Access_Level whenever the
      --  call occurs as part of a static accessibility check and the error
      --  case is the case where the type's level is too shallow (as opposed
      --  to too deep).

      if Is_Generic_Type (Root_Type (Btyp)) then
         return Scope_Depth (Standard_Standard);
      end if;

      return Scope_Depth (Enclosing_Dynamic_Scope (Btyp));
   end Type_Access_Level;

end Accessibility;