-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-- Public License for more details. You should have received a copy of the GNU
-- General Public License distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

separate (Sem.CompUnit.WalkStatements)
procedure Wf_Return
  (Node           : in     STree.SyntaxNode;
   Scope          : in     Dictionary.Scopes;
   Table          : in out RefList.HashTable;
   Component_Data : in out ComponentManager.ComponentData)
is
   Subprog_Sym, Expected_Type : Dictionary.Symbol;
   Result_Type                : Exp_Record;
   Ref_Var                    : SeqAlgebra.Seq;
   Child                      : STree.SyntaxNode;
begin
   -- conditions of Section 5.8 of the SPARK definition apply to this statement.

   SeqAlgebra.CreateSeq (TheHeap, Ref_Var);
   Subprog_Sym := Dictionary.GetEnclosingCompilationUnit (Scope);

   if Dictionary.IsProcedure (Subprog_Sym) then
      ErrorHandler.Control_Flow_Error (Err_Type => ErrorHandler.Return_In_Proc,
                                       Position => Node_Position (Node => Node));
   else -- subprogram is a function, check location of return within it;
      case Syntax_Node_Type (Node => Parent_Of_Sequence (Node => Node)) is
         when SP_Symbols.if_statement               |
           SP_Symbols.elsif_part                 |
           SP_Symbols.else_part                  |
           SP_Symbols.loop_statement             |
           SP_Symbols.case_statement_alternative |
           SP_Symbols.others_part                =>
            ErrorHandler.Control_Flow_Error (Err_Type => ErrorHandler.Misplaced_Return,
                                             Position => Node_Position (Node => Node));
         when others =>
            if not Is_Last_In_Sequence (Node => Node) then
               ErrorHandler.Control_Flow_Error
                 (Err_Type => ErrorHandler.Misplaced_Return,
                  Position => Node_Position (Node => Node));
            end if;
      end case;
      -- seed syntax tree with expected type for run-time check
      Expected_Type := Dictionary.GetType (Subprog_Sym);
      STree.Add_Node_Symbol (Node => Node,
                             Sym  => Expected_Type);

      Child := Child_Node (Current_Node => Node);
      -- ASSUME Child = expression
      SystemErrors.RT_Assert
        (C       => Syntax_Node_Type (Node => Child) = SP_Symbols.expression,
         Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect Child = expression in Wf_Condition");

      -- now check return type is correct
      Walk_Expression_P.Walk_Expression
        (Exp_Node                => Child,
         Scope                   => Scope,
         Type_Context            => Expected_Type,
         Context_Requires_Static => False,
         Ref_Var                 => Ref_Var,
         Result                  => Result_Type,
         Component_Data          => Component_Data,
         The_Heap                => TheHeap);
      Assignment_Check
        (Position    => Node_Position (Node => Node),
         Scope       => Scope,
         Target_Type => Expected_Type,
         Exp_Result  => Result_Type);

      -- if expression represents an IN stream variable then issue warning about
      -- possible validity problems if it is not marked valid
      if Result_Type.Is_AVariable
        and then Dictionary.GetOwnVariableOrConstituentMode (Result_Type.Variable_Symbol) = Dictionary.InMode then
         -- Check to see if the variable has been marked valid. Note that the
         -- OtherSymbol is checked,not the variableSymbol, since this will be
         -- the Subcomponent symbol if we are referring to a record component
         if Dictionary.VariableOrSubcomponentIsMarkedValid (Result_Type.Other_Symbol) then
            -- MCA: do we also need to add a use of 'Valid to the summary?
            -- Debug.PrintSym ("Return Access is Valid =", Result_Type.OtherSymbol);
            null;
         else
            -- The warning is stronger when the external variable is a type that doesn't
            -- generate run-time checks
            if Dictionary.TypeIsScalar (Result_Type.Type_Symbol)
              and then not Dictionary.TypeIsBoolean (Result_Type.Type_Symbol) then
               -- weaker warning
               ErrorHandler.Semantic_Warning_Sym
                 (Err_Num  => 392,
                  Position => Node_Position (Node => Child),
                  Sym      => Result_Type.Other_Symbol,
                  Scope    => Scope);
            else
               -- stronger warning
               ErrorHandler.Semantic_Warning_Sym
                 (Err_Num  => 393,
                  Position => Node_Position (Node => Child),
                  Sym      => Result_Type.Other_Symbol,
                  Scope    => Scope);
            end if;
         end if;
      end if;
   end if;
   -- add reference variable list to RefList hash table
   RefList.AddRelation (Table, TheHeap, Node, Dictionary.NullSymbol, Ref_Var);
end Wf_Return;
