Commit 5f325af2 by Arnaud Charlet

[multiple changes]

2017-01-12  Yannick Moy  <moy@adacore.com>

	* exp_spark.adb (Expand_SPARK_Potential_Renaming): Fix sloc of copied
	subtree.

2017-01-12  Justin Squirek  <squirek@adacore.com>

	* exp_attr.adb (Expand_N_Attribute_Reference):
	Fix Finalization_Size case by properly resolving the type after
	rewritting the node.

2017-01-12  Hristian Kirtchev  <kirtchev@adacore.com>

	* exp_util.adb (Build_DIC_Procedure_Body): Semi-insert the body into
	the tree.
	(Build_DIC_Procedure_Declaration): Semi-insert the body into the tree.
	* binde.adb, exp_ch5.adb, sem_type.adb, sem.ads, sem_res.adb,
	exp_sel.ads: Minor reformatting.

2017-01-12  Justin Squirek  <squirek@adacore.com>

	* exp_ch6.adb (Expand_Call): Add guard to prevent
	invariant checks from being created for internally generated
	subprograms.

2017-01-12  Bob Duff  <duff@adacore.com>

	* lib-writ.ads: Remove incorrect comment.

2017-01-12  Javier Miranda  <miranda@adacore.com>

	* debug.adb (-gnatd.K): Enable generation of contract-only
	procedures in CodePeer mode.
	* contracts.adb (Build_And_Analyze_Contract_Only_Subprograms):
	New subprogram.
	(Analyze_Contracts): Generate contract-only procedures if -gnatdK is
	set.
	* scil_ll.ads, scil_ll.adb (Get_Contract_Only_Body_Name): New
	subprogram.
	(Get_Contract_Only_Missing_Body_Name): New subprogram.
	(Get_Contract_Only_Body): New subprogram.
	(Set_Contract_Only_Body): New subprogram.
	(Is_Contract_Only_Body): New subprogram.
	(Set_Is_Contract_Only_Body): New subprogram.
	(SCIL_Nodes): Replace table by hash-table.

From-SVN: r244356
parent 10c2c151
2017-01-12 Yannick Moy <moy@adacore.com>
* exp_spark.adb (Expand_SPARK_Potential_Renaming): Fix sloc of copied
subtree.
2017-01-12 Justin Squirek <squirek@adacore.com>
* exp_attr.adb (Expand_N_Attribute_Reference):
Fix Finalization_Size case by properly resolving the type after
rewritting the node.
2017-01-12 Hristian Kirtchev <kirtchev@adacore.com>
* exp_util.adb (Build_DIC_Procedure_Body): Semi-insert the body into
the tree.
(Build_DIC_Procedure_Declaration): Semi-insert the body into the tree.
* binde.adb, exp_ch5.adb, sem_type.adb, sem.ads, sem_res.adb,
exp_sel.ads: Minor reformatting.
2017-01-12 Justin Squirek <squirek@adacore.com>
* exp_ch6.adb (Expand_Call): Add guard to prevent
invariant checks from being created for internally generated
subprograms.
2017-01-12 Bob Duff <duff@adacore.com>
* lib-writ.ads: Remove incorrect comment.
2017-01-12 Javier Miranda <miranda@adacore.com>
* debug.adb (-gnatd.K): Enable generation of contract-only
procedures in CodePeer mode.
* contracts.adb (Build_And_Analyze_Contract_Only_Subprograms):
New subprogram.
(Analyze_Contracts): Generate contract-only procedures if -gnatdK is
set.
* scil_ll.ads, scil_ll.adb (Get_Contract_Only_Body_Name): New
subprogram.
(Get_Contract_Only_Missing_Body_Name): New subprogram.
(Get_Contract_Only_Body): New subprogram.
(Set_Contract_Only_Body): New subprogram.
(Is_Contract_Only_Body): New subprogram.
(Set_Is_Contract_Only_Body): New subprogram.
(SCIL_Nodes): Replace table by hash-table.
2017-01-12 Hristian Kirtchev <kirtchev@adacore.com>
* exp_ch6.adb: Minor reformatting.
......
......@@ -1211,14 +1211,18 @@ package body Binde is
-- There is a lot of fiddly string manipulation below, because we don't
-- want to depend on misc utility packages like Ada.Characters.Handling.
function Read_File (Name : String) return String_Ptr;
-- Read the entire contents of the named file
function Get_Line return String;
-- Read the next line from the file content read by Read_File. Strip
-- leading and trailing blanks. Convert "(spec)" or "(body)" to
-- "%s"/"%b". Remove comments (Ada style; "--" to end of line).
function Read_File (Name : String) return String_Ptr;
-- Read the entire contents of the named file
---------------
-- Read_File --
---------------
function Read_File (Name : String) return String_Ptr is
-- All of the following calls should succeed, because we checked the
-- file in Switch.B, but we double check and raise Program_Error on
......@@ -1232,9 +1236,11 @@ package body Binde is
end if;
declare
Len : constant Natural := Natural (File_Length (F));
Result : constant String_Ptr := new String (1 .. Len);
Len_Read : constant Natural := Read (F, Result (1)'Address, Len);
Len : constant Natural := Natural (File_Length (F));
Result : constant String_Ptr := new String (1 .. Len);
Len_Read : constant Natural :=
Read (F, Result (1)'Address, Len);
Status : Boolean;
begin
......@@ -1252,12 +1258,17 @@ package body Binde is
end;
end Read_File;
S : String_Ptr := Read_File (Force_Elab_Order_File.all);
Cur : Positive := 1;
Cur : Positive := 1;
S : String_Ptr := Read_File (Force_Elab_Order_File.all);
--------------
-- Get_Line --
--------------
function Get_Line return String is
First : Positive := Cur;
Last : Natural;
Last : Natural;
begin
-- Skip to end of line
......@@ -1293,12 +1304,16 @@ package body Binde is
-- again.
declare
Body_String : constant String := "(body)";
BL : constant Positive := Body_String'Length;
Spec_String : constant String := "(spec)";
SL : constant Positive := Spec_String'Length;
Line : String renames S (First .. Last);
Spec_String : constant String := "(spec)";
SL : constant Positive := Spec_String'Length;
Body_String : constant String := "(body)";
BL : constant Positive := Body_String'Length;
Is_Spec, Is_Body : Boolean := False;
Is_Body : Boolean := False;
Is_Spec : Boolean := False;
begin
if Line'Length >= SL
and then Line (Last - SL + 1 .. Last) = Spec_String
......@@ -1336,8 +1351,12 @@ package body Binde is
end;
end Get_Line;
-- Local variables
Empty_Name : constant Unit_Name_Type := Name_Find ("");
Prev_Unit : Unit_Id := No_Unit_Id;
Prev_Unit : Unit_Id := No_Unit_Id;
-- Start of processing for Force_Elab_Order
begin
-- Loop through the file content, and build a dependency link for each
......
......@@ -128,7 +128,7 @@ package body Debug is
-- d.H GNSA mode for ASIS
-- d.I Do not ignore enum representation clauses in CodePeer mode
-- d.J Disable parallel SCIL generation mode
-- d.K
-- d.K Enable generation of contract-only procedures in CodePeer mode
-- d.L Depend on back end for limited types in if and case expressions
-- d.M Relaxed RM semantics
-- d.N Add node to all entities
......@@ -646,6 +646,13 @@ package body Debug is
-- done in parallel to speed processing. This switch disables this
-- behavior.
-- d.K Enable generation of contract-only procedures in CodePeer mode and
-- report a warning on subprograms for which the contract-only body
-- cannot be built. Currently reported on subprograms defined in
-- nested package specs that have some formal (or return type) whose
-- type is a private type defined in some enclosing package and that
-- have pre/postconditions.
-- d.L Normally the front end generates special expansion for conditional
-- expressions of a limited type. This debug flag removes this special
-- case expansion, leaving it up to the back end to handle conditional
......
......@@ -3244,7 +3244,11 @@ package body Exp_Attr is
Rewrite (N, Make_Integer_Literal (Loc, 0));
end if;
Analyze (N);
-- Due to cases where the entity type of the attribute is already
-- resolved the rewritten N must get re-resolved to its appropriate
-- type.
Analyze_And_Resolve (N, Typ);
end Finalization_Size;
-----------
......
......@@ -2939,7 +2939,7 @@ package body Exp_Ch5 is
-- For an element iterator, the Element aspect must be present,
-- (this is checked during analysis) and the expansion takes the form:
-- Cursor : Cursor_type := First (Container);
-- Cursor : Cursor_Type := First (Container);
-- Elmt : Element_Type;
-- while Has_Element (Cursor, Container) loop
-- Elmt := Element (Container, Cursor);
......@@ -2951,10 +2951,10 @@ package body Exp_Ch5 is
-- In that case we create a block to hold a variable declaration
-- initialized with a call to Element, and generate:
-- Cursor : Cursor_type := First (Container);
-- Cursor : Cursor_Type := First (Container);
-- while Has_Element (Cursor, Container) loop
-- declare
-- Elmt : Element-Type := Element (Container, Cursor);
-- Elmt : Element_Type := Element (Container, Cursor);
-- begin
-- <original loop statements>
-- Cursor := Next (Container, Cursor);
......@@ -2968,7 +2968,7 @@ package body Exp_Ch5 is
Set_Ekind (Cursor, E_Variable);
Insert_Action (N, Init);
-- Declaration for Element.
-- Declaration for Element
Elmt_Decl :=
Make_Object_Declaration (Loc,
......
......@@ -2264,7 +2264,9 @@ package body Exp_Ch6 is
-- expression for the value of the actual, EF is the entity for the
-- extra formal.
procedure Check_View_Conversion (Formal : Entity_Id; Actual : Node_Id);
procedure Add_View_Conversion_Invariants
(Formal : Entity_Id;
Actual : Node_Id);
-- Adds invariant checks for every intermediate type between the range
-- of a view converted argument to its ancestor (from parent to child).
......@@ -2354,11 +2356,14 @@ package body Exp_Ch6 is
end if;
end Add_Extra_Actual;
---------------------------
-- Check_View_Conversion --
---------------------------
------------------------------------
-- Add_View_Conversion_Invariants --
------------------------------------
procedure Check_View_Conversion (Formal : Entity_Id; Actual : Node_Id) is
procedure Add_View_Conversion_Invariants
(Formal : Entity_Id;
Actual : Node_Id)
is
Arg : Entity_Id;
Curr_Typ : Entity_Id;
Inv_Checks : List_Id;
......@@ -2407,7 +2412,7 @@ package body Exp_Ch6 is
if not Is_Empty_List (Inv_Checks) then
Insert_Actions_After (N, Inv_Checks);
end if;
end Check_View_Conversion;
end Add_View_Conversion_Invariants;
---------------------------
-- Inherited_From_Formal --
......@@ -3292,15 +3297,18 @@ package body Exp_Ch6 is
Duplicate_Subexpr_Move_Checks (Actual)));
end if;
-- Invariant checks are performed for every intermediate type between
-- the range of a view converted argument to its ancestor (from
-- parent to child) if it is passed as an "out" or "in out" parameter
-- after executing the call (RM 7.3.2 (12/3, 13/3, 14/3)).
-- Perform invariant checks for all intermediate types in a view
-- conversion after successful return from a call that passes the
-- view conversion as an IN OUT or OUT parameter (RM 7.3.2 (12/3,
-- 13/3, 14/3)). Consider only source conversion in order to avoid
-- generating spurious checks on complex expansion such as object
-- initialization through an extension aggregate.
if Ekind (Formal) /= E_In_Parameter
if Comes_From_Source (N)
and then Ekind (Formal) /= E_In_Parameter
and then Nkind (Actual) = N_Type_Conversion
then
Check_View_Conversion (Formal, Actual);
Add_View_Conversion_Invariants (Formal, Actual);
end if;
-- This label is required when skipping extra actual generation for
......
......@@ -6,7 +6,7 @@
-- --
-- S p e c --
-- --
-- Copyright (C) 1992-2011, Free Software Foundation, Inc. --
-- Copyright (C) 1992-2016, 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- --
......@@ -48,7 +48,7 @@ package Exp_Sel is
function Build_Abort_Block_Handler (Loc : Source_Ptr) return Node_Id;
-- Generate if front-end exception:
-- when others =>
-- Abort_Under;
-- Abort_Undefer;
-- or if back-end exception:
-- when others =>
-- null;
......
......@@ -140,7 +140,7 @@ package body Exp_SPARK is
-- Otherwise the renamed object denotes a name
else
Rewrite (N, New_Copy_Tree (Obj_Id));
Rewrite (N, New_Copy_Tree (Obj_Id, New_Sloc => Loc));
Reset_Analyzed_Flags (N);
end if;
......
......@@ -1897,13 +1897,19 @@ package body Exp_Util is
Set_Corresponding_Spec (Proc_Body, Proc_Id);
-- The body should not be inserted into the tree when the context is
-- ASIS, GNATprove or a generic unit because it is not part of the
-- template. Note that the body must still be generated in order to
-- resolve the DIC assertion expression.
-- ASIS or a generic unit because it is not part of the template. Note
-- that the body must still be generated in order to resolve the DIC
-- assertion expression.
if ASIS_Mode or GNATprove_Mode or Inside_A_Generic then
if ASIS_Mode or Inside_A_Generic then
null;
-- Semi-insert the body into the tree for GNATprove by setting its
-- Parent field. This allows for proper upstream tree traversals.
elsif GNATprove_Mode then
Set_Parent (Proc_Body, Parent (Declaration_Node (Work_Typ)));
-- Otherwise the body is part of the freezing actions of the working
-- type.
......@@ -2083,16 +2089,20 @@ package body Exp_Util is
New_Occurrence_Of (Work_Typ, Loc)))));
-- The declaration should not be inserted into the tree when the context
-- is ASIS, GNATprove, or a generic unit because it is not part of the
-- template.
-- is ASIS or a generic unit because it is not part of the template.
if ASIS_Mode or GNATprove_Mode or Inside_A_Generic then
if ASIS_Mode or Inside_A_Generic then
null;
-- Semi-insert the declaration into the tree for GNATprove by setting
-- its Parent field. This allows for proper upstream tree traversals.
elsif GNATprove_Mode then
Set_Parent (Proc_Decl, Parent (Typ_Decl));
-- Otherwise insert the declaration
else
pragma Assert (Present (Typ_Decl));
Insert_After_And_Analyze (Typ_Decl, Proc_Decl);
end if;
......
......@@ -649,10 +649,8 @@ package Lib.Writ is
-- AD Elaborate_All_Desirable set for this unit, which means that
-- there is no Elaborate_All, but the analysis suggests that
-- Program_Error may be raised if the Elaborate_All conditions
-- cannot be satisfied. In dynamic elaboration mode, the binder
-- will attempt to treat AD as EA if it can. In static
-- elaboration mode, the binder will treat AD as EA, even if it
-- introduces cycles.
-- cannot be satisfied. The binder will attempt to treat AD as
-- EA if it can.
-- The parameter source-name and lib-name are omitted for the case of a
-- generic unit compiled with earlier versions of GNAT which did not
......
......@@ -6,7 +6,7 @@
-- --
-- B o d y --
-- --
-- Copyright (C) 2010-2012, Free Software Foundation, Inc. --
-- Copyright (C) 2010-2016, 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- --
......@@ -29,63 +29,105 @@
-- --
------------------------------------------------------------------------------
with Alloc; use Alloc;
with Atree; use Atree;
with Opt; use Opt;
with Sinfo; use Sinfo;
with Table;
with Atree; use Atree;
with Opt; use Opt;
with Sinfo; use Sinfo;
with System.HTable; use System.HTable;
package body SCIL_LL is
Contract_Only_Body_Suffix : constant String := "__contract_only";
-- Suffix of Contract_Only_Body subprograms internally built only under
-- CodePeer mode
Contract_Only_Missing_Body_Suffix : constant String := "__missing_body";
-- Suffix of Contract_Only_Missing_Body subprograms internally built only
-- under CodePeer mode
procedure Copy_SCIL_Node (Target : Node_Id; Source : Node_Id);
-- Copy the SCIL field from Source to Target (it is used as the argument
-- for a call to Set_Reporting_Proc in package atree).
function SCIL_Nodes_Table_Size return Pos;
-- Used to initialize the table of SCIL nodes because we do not want
-- to consume memory for this table if it is not required.
type Header_Num is range 1 .. 4096;
function Hash (N : Node_Id) return Header_Num;
-- Hash function for Node_Ids
--------------------------
-- Internal Hash Tables --
--------------------------
package Contract_Only_Body_Flag is new Simple_HTable
(Header_Num => Header_Num,
Element => Boolean,
No_Element => False,
Key => Node_Id,
Hash => Hash,
Equal => "=");
-- This table records the value of flag Is_Contract_Only_Flag of tree nodes
package Contract_Only_Body_Nodes is new Simple_HTable
(Header_Num => Header_Num,
Element => Node_Id,
No_Element => Empty,
Key => Node_Id,
Hash => Hash,
Equal => "=");
-- This table records the value of attribute Contract_Only_Body of tree
-- nodes.
package SCIL_Nodes is new Simple_HTable
(Header_Num => Header_Num,
Element => Node_Id,
No_Element => Empty,
Key => Node_Id,
Hash => Hash,
Equal => "=");
-- This table records the value of attribute SCIL_Node of tree nodes.
--------------------
-- Copy_SCIL_Node --
--------------------
procedure Copy_SCIL_Node (Target : Node_Id; Source : Node_Id) is
begin
Set_SCIL_Node (Target, Get_SCIL_Node (Source));
end Copy_SCIL_Node;
----------------------------
-- SCIL_Nodes_Table_Size --
-- Get_Contract_Only_Body --
----------------------------
function SCIL_Nodes_Table_Size return Pos is
function Get_Contract_Only_Body (N : Node_Id) return Node_Id is
begin
if Generate_SCIL then
return Alloc.Orig_Nodes_Initial;
if CodePeer_Mode
and then Present (N)
then
return Contract_Only_Body_Nodes.Get (N);
else
return 1;
return Empty;
end if;
end SCIL_Nodes_Table_Size;
package SCIL_Nodes is new Table.Table (
Table_Component_Type => Node_Id,
Table_Index_Type => Node_Id'Base,
Table_Low_Bound => First_Node_Id,
Table_Initial => SCIL_Nodes_Table_Size,
Table_Increment => Alloc.Orig_Nodes_Increment,
Table_Name => "SCIL_Nodes");
-- This table records the value of attribute SCIL_Node of all the
-- tree nodes.
end Get_Contract_Only_Body;
--------------------
-- Copy_SCIL_Node --
--------------------
---------------------------------
-- Get_Contract_Only_Body_Name --
---------------------------------
procedure Copy_SCIL_Node (Target : Node_Id; Source : Node_Id) is
function Get_Contract_Only_Body_Name (E : Entity_Id) return Name_Id is
begin
Set_SCIL_Node (Target, Get_SCIL_Node (Source));
end Copy_SCIL_Node;
return Name_Find (Get_Name_String (Chars (E)) &
Contract_Only_Body_Suffix);
end Get_Contract_Only_Body_Name;
----------------
-- Initialize --
----------------
-----------------------------------------
-- Get_Contract_Only_Missing_Body_Name --
-----------------------------------------
procedure Initialize is
function Get_Contract_Only_Missing_Body_Name (E : Entity_Id)
return Name_Id is
begin
SCIL_Nodes.Init;
Set_Reporting_Proc (Copy_SCIL_Node'Access);
end Initialize;
return Name_Find (Get_Name_String (Chars (E)) &
Contract_Only_Missing_Body_Suffix);
end Get_Contract_Only_Missing_Body_Name;
-------------------
-- Get_SCIL_Node --
......@@ -96,12 +138,64 @@ package body SCIL_LL is
if Generate_SCIL
and then Present (N)
then
return SCIL_Nodes.Table (N);
return SCIL_Nodes.Get (N);
else
return Empty;
end if;
end Get_SCIL_Node;
----------
-- Hash --
----------
function Hash (N : Node_Id) return Header_Num is
begin
return Header_Num (1 + N mod Node_Id (Header_Num'Last));
end Hash;
----------------
-- Initialize --
----------------
procedure Initialize is
begin
SCIL_Nodes.Reset;
Contract_Only_Body_Nodes.Reset;
Contract_Only_Body_Flag.Reset;
Set_Reporting_Proc (Copy_SCIL_Node'Access);
end Initialize;
---------------------------
-- Is_Contract_Only_Body --
---------------------------
function Is_Contract_Only_Body (E : Entity_Id) return Boolean is
begin
return Contract_Only_Body_Flag.Get (E);
end Is_Contract_Only_Body;
----------------------------
-- Set_Contract_Only_Body --
----------------------------
procedure Set_Contract_Only_Body (N : Node_Id; Value : Node_Id) is
begin
pragma Assert (CodePeer_Mode
and then Present (N)
and then Is_Contract_Only_Body (Value));
Contract_Only_Body_Nodes.Set (N, Value);
end Set_Contract_Only_Body;
-------------------------------
-- Set_Is_Contract_Only_Body --
-------------------------------
procedure Set_Is_Contract_Only_Body (E : Entity_Id) is
begin
Contract_Only_Body_Flag.Set (E, True);
end Set_Is_Contract_Only_Body;
-------------------
-- Set_SCIL_Node --
-------------------
......@@ -133,11 +227,7 @@ package body SCIL_LL is
end case;
end if;
if Atree.Last_Node_Id > SCIL_Nodes.Last then
SCIL_Nodes.Set_Last (Atree.Last_Node_Id);
end if;
SCIL_Nodes.Set_Item (N, Value);
SCIL_Nodes.Set (N, Value);
end Set_SCIL_Node;
end SCIL_LL;
......@@ -6,7 +6,7 @@
-- --
-- S p e c --
-- --
-- Copyright (C) 2010, Free Software Foundation, Inc. --
-- Copyright (C) 2010-2016, 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- --
......@@ -29,20 +29,39 @@
-- --
------------------------------------------------------------------------------
-- This package extends the tree nodes with a field that is used to reference
-- the SCIL node.
-- This package extends the tree nodes with fields that are used to reference
-- the SCIL node and the Contract_Only_Body of a subprogram with aspects.
with Namet; use Namet;
with Types; use Types;
package SCIL_LL is
function Get_Contract_Only_Body_Name (E : Entity_Id) return Name_Id;
-- Return the name of the Contract_Only_Body subprogram of E
function Get_Contract_Only_Missing_Body_Name (E : Entity_Id) return Name_Id;
-- Return the name of the Contract_Only_Missing_Body subprogram of E
function Get_Contract_Only_Body (N : Node_Id) return Node_Id;
-- Read the value of attribute Contract_Only_Body
function Get_SCIL_Node (N : Node_Id) return Node_Id;
-- Read the value of attribute SCIL node
procedure Set_Contract_Only_Body (N : Node_Id; Value : Node_Id);
-- Set the value of attribute Contract_Only_Body
procedure Set_SCIL_Node (N : Node_Id; Value : Node_Id);
-- Set the value of attribute SCIL node
procedure Initialize;
-- Initialize the table of SCIL nodes
function Is_Contract_Only_Body (E : Entity_Id) return Boolean;
-- Return True if E is a Contract_Only_Body subprogram
procedure Set_Is_Contract_Only_Body (E : Entity_Id);
-- Set E as Contract_Only_Body subprogram
end SCIL_LL;
......@@ -523,8 +523,8 @@ package Sem is
-- See Sem_Ch10 (Install_Parents, Remove_Parents).
Node_To_Be_Wrapped : Node_Id;
-- Only used in transient scopes. Records the node which will
-- be wrapped by the transient block.
-- Only used in transient scopes. Records the node which will be wrapped
-- by the transient block.
Actions_To_Be_Wrapped : Scope_Actions;
-- Actions that have to be inserted at the start, at the end, or as
......
......@@ -11810,8 +11810,7 @@ package body Sem_Res is
-- Valid_Array_Conversion --
----------------------------
function Valid_Array_Conversion return Boolean
is
function Valid_Array_Conversion return Boolean is
Opnd_Comp_Type : constant Entity_Id := Component_Type (Opnd_Type);
Opnd_Comp_Base : constant Entity_Id := Base_Type (Opnd_Comp_Type);
......
......@@ -307,7 +307,6 @@ package body Sem_Type is
else
Get_Next_Interp (I, It);
end if;
end loop;
All_Interp.Table (All_Interp.Last) := (Name, Typ, Abstr_Op);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment