Commit a83d0680 by Arnaud Charlet

[multiple changes]

2017-09-11  Arnaud Charlet  <charlet@adacore.com>

	* doc/gnat_ugn/the_gnat_compilation_model.rst: Fix sphinx warning.
	* doc/gnat_ugn/platform_specific_information.rst: Remove doc
	for no longer supported platforms.
	* doc/gnat_ugn/gnat_and_program_execution.rst: Added detailed
	description of the semantics for dimensionality analysis.
	* gnat_ugn.texi: Regenerated.

2017-09-11  Eric Botcazou  <ebotcazou@adacore.com>

	* freeze.adb (Has_Incomplete_Compoent): Delete.
	(Freeze_Profile):
	Do not inhibit the freezing of the profile of an expression
	function here.
	(Freeze_Subprogram): Do not re-create extra formals.
	* sem_ch6.adb (Analyze_Expression_Function): Always
	pre-analyze the expression if the function is not a completion.
	(Analyze_Subprogram_Body_Helper): For the body generated
	from an expression function that is not a completion, do
	not freeze the profile and temporary mask the types declared
	outside the expression that are not yet frozen.
	* sem_res.adb (Rewrite_Renamed_Operator): Also bail out if invoked
	during the pre-analysis of an expression function.

From-SVN: r251978
parent c65e7627
2017-09-11 Arnaud Charlet <charlet@adacore.com>
* doc/gnat_ugn/the_gnat_compilation_model.rst: Fix sphinx warning.
* doc/gnat_ugn/platform_specific_information.rst: Remove doc
for no longer supported platforms.
* doc/gnat_ugn/gnat_and_program_execution.rst: Added detailed
description of the semantics for dimensionality analysis.
* gnat_ugn.texi: Regenerated.
2017-09-11 Eric Botcazou <ebotcazou@adacore.com>
* freeze.adb (Has_Incomplete_Compoent): Delete.
(Freeze_Profile):
Do not inhibit the freezing of the profile of an expression
function here.
(Freeze_Subprogram): Do not re-create extra formals.
* sem_ch6.adb (Analyze_Expression_Function): Always
pre-analyze the expression if the function is not a completion.
(Analyze_Subprogram_Body_Helper): For the body generated
from an expression function that is not a completion, do
not freeze the profile and temporary mask the types declared
outside the expression that are not yet frozen.
* sem_res.adb (Rewrite_Renamed_Operator): Also bail out if invoked
during the pre-analysis of an expression function.
2017-09-11 Eric Botcazou <ebotcazou@adacore.com>
* gcc-interface/gigi.h (enum standard_datatypes): Minor tweak.
......
......@@ -3545,6 +3545,134 @@ then the output includes:
Final velocity: 98.10 m.s**(-1)
.. index:: Dimensionable type
.. index:: Dimensioned subtype
The type ``Mks_Type`` is said to be a *dimensionable type* since it has a
``Dimension_System`` aspect, and the subtypes ``Length``, ``Mass``, etc.,
are said to be *dimensioned subtypes* since each one has a ``Dimension``
aspect.
.. index:: Dimension Vector (for a dimensioned subtype)
.. index:: Dimension aspect
.. index:: Dimension_System aspect
The ``Dimension`` aspect of a dimensioned subtype ``S`` defines a mapping
from the base type's Unit_Names to integer (or, more generally, rational)
values. This mapping is the *dimension vector* (also referred to as the
*dimensionality*) for that subtype, denoted by ``DV(S)``, and thus for each
object of that subtype. Intuitively, the value specified for each
``Unit_Name`` is the exponent associated with that unit; a zero value
means that the unit is not used. For example:
.. code-block:: ada
declare
Acc : Acceleration;
...
begin
...
end;
Here ``DV(Acc)`` = ``DV(Acceleration)`` =
``(Meter=>1, Kilogram=>0, Second => -2, Ampere=>0, Kelvin=>0, Mole=>0, Candela => 0)``.
Symbolically, we can express this as ``Meter / Second**2``.
The dimension vector of an arithmetic expression is synthesized from the
dimension vectors of its components, with compile-time dimensionality checks
that help prevent mismatches such as using an ``Acceleration`` where a
``Length`` is required.
The dimension vector of the result of an arithmetic expression *expr*, or
:samp:`DV({expr})`, is defined as follows, assuming conventional
mathematical definitions for the vector operations that are used:
* If *expr* is of the type *universal_real*, or is not of a dimensioned subtype,
then *expr* is dimensionless; :samp:`DV({expr})` is the empty vector.
* :samp:`DV({op expr})`, where *op* is a unary operator, is :samp:`DV({expr})`
* :samp:`DV({expr1 op expr2})` where *op* is "+" or "-" is :samp:`DV({expr1})`
provided that :samp:`DV({expr1})` = :samp:`DV({expr2})`.
If this condition is not met then the construct is illegal.
* :samp:`DV({expr1} * {expr2})` is :samp:`DV({expr1})` + :samp:`DV({expr2})`,
and :samp:`DV({expr1} / {expr2})` = :samp:`DV({expr1})` - :samp:`DV({expr2})`.
In this context if one of the *expr*\ s is dimensionless then its empty
dimension vector is treated as ``(others => 0)``.
* :samp:`DV({expr} ** {power})` is *power* * :samp:`DV({expr})`,
provided that *power* is a static rational value. If this condition is not
met then the construct is illegal.
Note that, by the above rules, it is illegal to use binary "+" or "-" to
combine a dimensioned and dimensionless value. Thus an expression such as
``acc-10.0`` is illegal, where ``acc`` is an object of subtype
``Acceleration``.
The dimensionality checks for relationals use the same rules as
for "+" and "-"; thus
.. code-block:: ada
acc > 10.0
is equivalent to
.. code-block:: ada
acc-10.0 > 0.0
and is thus illegal. Analogously a conditional expression
requires the same dimension vector for each branch.
The dimension vector of a type conversion :samp:`T({expr})` is defined
as follows, based on the nature of ``T``:
* If ``T`` is a dimensioned subtype then :samp:`DV(T({expr}))` is ``DV(T)``
provided that either *expr* is dimensionless or
:samp:`DV(T)` = :samp:`DV({expr})`. The conversion is illegal
if *expr* is dimensioned and :samp:`DV({expr})` /= ``DV(T)``.
Note that vector equality does not require that the corresponding
Unit_Names be the same.
As a consequence of the above rule, it is possible to convert between
different dimension systems that follow the same international system
of units, with the seven physical components given in the standard order
(length, mass, time, etc.). Thus a length in meters can be converted to
a length in inches (with a suitable conversion factor) but cannot be
converted, for example, to a mass in pounds.
* If ``T`` is the base type for *expr* (and the dimensionless root type of
the dimension system), then :samp:`DV(T({expr}))` is ``DV(expr)``.
Thus, if *expr* is of a dimensioned subtype of ``T``, the conversion may
be regarded as a "view conversion" that preserves dimensionality.
This rule makes it possible to write generic code that can be instantiated
with compatible dimensioned subtypes. The generic unit will contain
conversions that will consequently be present in instantiations, but
conversions to the base type will preserve dimensionality and make it
possible to write generic code that is correct with respect to
dimensionality.
* Otherwise (i.e., ``T`` is neither a dimensioned subtype nor a dimensionable
base type), :samp:`DV(T({expr}))` is the empty vector. Thus a dimensioned
value can be explicitly converted to a non-dimensioned subtype, which
of course then escapes dimensionality analysis.
The dimension vector for a type qualification :samp:`T'({expr})` is the same
as for the type conversion :samp:`T({expr})`.
An assignment statement
.. code-block:: ada
Source := Target;
requires ``DV(Source)`` = ``DV(Target)``, and analogously for parameter
passing (the dimension vector for the actual parameter must be equal to the
dimension vector for the formal parameter).
.. _Stack_Related_Facilities:
......
......@@ -26,14 +26,9 @@ Run-Time Libraries
.. index:: Run-time libraries (platform-specific information)
The GNAT run-time implementation may vary with respect to both the
underlying threads library and the exception handling scheme.
For threads support, one or more of the following are supplied:
* **native threads library**, a binding to the thread package from
the underlying operating system
* **pthreads library** (Sparc Solaris only), a binding to the Solaris
POSIX thread package
underlying threads library and the exception-handling scheme.
For threads support, the default run-time will bind to the thread
package of the underlying operating system.
For exception handling, either or both of two models are supplied:
......@@ -72,45 +67,17 @@ Summary of Run-Time Configurations
+-----------------+--------------+-------------------------+------------+
| Platform | Run-Time | Tasking | Exceptions |
+=================+==============+=========================+============+
| ppc-aix | rts-native | native AIX threads | ZCX |
| | (default) | | |
| +--------------+-------------------------+------------+
| | rts-sjlj | native AIX threads | SJLJ |
+-----------------+--------------+-------------------------+------------+
| sparc-solaris | rts-native | native Solaris | ZCX |
| | (default) | threads library | |
| +--------------+-------------------------+------------+
| | rts-pthread | pthread library | ZCX |
| +--------------+-------------------------+------------+
| | rts-sjlj | native Solaris | SJLJ |
| | | threads library | |
+-----------------+--------------+-------------------------+------------+
| sparc64-solaris | rts-native | native Solaris | ZCX |
| | (default) | threads library | |
+-----------------+--------------+-------------------------+------------+
| x86-linux | rts-native | pthread library | ZCX |
| GNU/Linux | rts-native | pthread library | ZCX |
| | (default) | | |
| +--------------+-------------------------+------------+
| | rts-sjlj | pthread library | SJLJ |
+-----------------+--------------+-------------------------+------------+
| x86-lynx | rts-native | native LynxOS threads | SJLJ |
| | (default) | | |
+-----------------+--------------+-------------------------+------------+
| x86-solaris | rts-native | native Solaris | ZCX |
| | (default) | threads library | |
| +--------------+-------------------------+------------+
| | rts-sjlj | native Solaris | SJLJ |
| | | threads library | |
+-----------------+--------------+-------------------------+------------+
| x86-windows | rts-native | native Win32 threads | ZCX |
| Windows | rts-native | native Win32 threads | ZCX |
| | (default) | | |
| +--------------+-------------------------+------------+
| | rts-sjlj | native Win32 threads | SJLJ |
+-----------------+--------------+-------------------------+------------+
| x86_64-linux | rts-native | pthread library | ZCX |
| | (default) | | |
| +--------------+-------------------------+------------+
| | rts-sjlj | pthread library | SJLJ |
| Mac OS | rts-native | pthread library | ZCX |
+-----------------+--------------+-------------------------+------------+
......@@ -136,7 +103,7 @@ below explains the differences between the different libraries in terms of
their thread support.
The default run-time library (when GNAT is installed) is *rts-native*.
This default run time is selected by the means of soft links.
This default run-time is selected by the means of soft links.
For example on x86-linux::
--
......@@ -252,95 +219,7 @@ this in a library package body in your application:
It gets the effective user id, and if it's not 0 (i.e. root), it raises
Program_Error.
.. index:: Solaris Sparc threads libraries
.. _Solaris-Specific_Considerations:
Solaris-Specific Considerations
-------------------------------
This section addresses some topics related to the various threads libraries
on Sparc Solaris.
.. index:: rts-pthread threads library
.. _Solaris_Threads_Issues:
Solaris Threads Issues
----------------------
GNAT under Solaris/Sparc 32 bits comes with an alternate tasking run-time
library based on POSIX threads --- *rts-pthread*.
.. index:: PTHREAD_PRIO_INHERIT policy (under rts-pthread)
.. index:: PTHREAD_PRIO_PROTECT policy (under rts-pthread)
.. index:: pragma Locking_Policy (under rts-pthread)
.. index:: Inheritance_Locking (under rts-pthread)
.. index:: Ceiling_Locking (under rts-pthread)
This run-time library has the advantage of being mostly shared across all
POSIX-compliant thread implementations, and it also provides under
Solaris |nbsp| 8 the ``PTHREAD_PRIO_INHERIT``
and ``PTHREAD_PRIO_PROTECT``
semantics that can be selected using the predefined pragma
``Locking_Policy``
with respectively
``Inheritance_Locking`` and ``Ceiling_Locking`` as the policy.
As explained above, the native run-time library is based on the Solaris thread
library (``libthread``) and is the default library.
.. index:: GNAT_PROCESSOR environment variable (on Sparc Solaris)
When the Solaris threads library is used (this is the default), programs
compiled with GNAT can automatically take advantage of
and can thus execute on multiple processors.
The user can alternatively specify a processor on which the program should run
to emulate a single-processor system. The multiprocessor / uniprocessor choice
is made by
setting the environment variable :envvar:`GNAT_PROCESSOR`
to one of the following:
========================= ===================================================================
``GNAT_PROCESSOR`` Value Effect
========================= ===================================================================
``-2`` Use the default configuration (run the program on all
available processors) - this is the same as having ``GNAT_PROCESSOR``
unset
``-1`` Let the run-time implementation choose one processor and run the
program on that processor
``0 .. Last_Proc`` Run the program on the specified processor.
``Last_Proc`` is equal to ``_SC_NPROCESSORS_CONF - 1``
(where ``_SC_NPROCESSORS_CONF`` is a system variable).
========================= ===================================================================
.. _AIX-Specific_Considerations:
AIX-Specific Considerations
---------------------------
.. index:: AIX resolver library
On AIX, the resolver library initializes some internal structure on
the first call to ``get*by*`` functions, which are used to implement
``GNAT.Sockets.Get_Host_By_Name`` and
``GNAT.Sockets.Get_Host_By_Address``.
If such initialization occurs within an Ada task, and the stack size for
the task is the default size, a stack overflow may occur.
To avoid this overflow, the user should either ensure that the first call
to ``GNAT.Sockets.Get_Host_By_Name`` or
``GNAT.Sockets.Get_Host_By_Addrss``
occurs in the environment task, or use ``pragma Storage_Size`` to
specify a sufficiently large size for the stack of the task that contains
this call.
.. index:: Windows NT
.. index:: Windows 95
.. index:: Windows 98
.. index:: Windows
.. _Microsoft_Windows_Topics:
......@@ -1252,11 +1131,11 @@ Limitations When Using Ada DLLs from Ada
""""""""""""""""""""""""""""""""""""""""
When using Ada DLLs from Ada applications there is a limitation users
should be aware of. Because on Windows the GNAT run time is not in a DLL of
its own, each Ada DLL includes a part of the GNAT run time. Specifically,
each Ada DLL includes the services of the GNAT run time that are necessary
should be aware of. Because on Windows the GNAT run-time is not in a DLL of
its own, each Ada DLL includes a part of the GNAT run-time. Specifically,
each Ada DLL includes the services of the GNAT run-time that are necessary
to the Ada code inside the DLL. As a result, when an Ada program uses an
Ada DLL there are two independent GNAT run times: one in the Ada DLL and
Ada DLL there are two independent GNAT run-times: one in the Ada DLL and
one in the main program.
It is therefore not possible to exchange GNAT run-time objects between the
......@@ -1395,7 +1274,7 @@ initialization routine. Unfortunately, it is not possible to call
``adainit`` from the ``DllMain`` if your program has library level
tasks because access to the ``DllMain`` entry point is serialized by
the system (that is, only a single thread can execute 'through' it at a
time), which means that the GNAT run time will deadlock waiting for the
time), which means that the GNAT run-time will deadlock waiting for the
newly created task to complete its initialization.
......
......@@ -3117,13 +3117,13 @@ Note that although the substitution of strings within a string literal
is not possible, it is possible to have a symbol whose defined value is
a string literal. So instead of setting XYZ to ``hello`` and writing:
.. code-block:: c
.. code-block:: ada
Header : String := "$XYZ";
you should set XYZ to ``"hello"`` and write:
.. code-block:: c
.. code-block:: ada
Header : String := $XYZ;
......
......@@ -3423,72 +3423,10 @@ package body Freeze is
--------------------
function Freeze_Profile (E : Entity_Id) return Boolean is
function Has_Incomplete_Component (T : Entity_Id) return Boolean;
-- If a type includes a private component from an enclosing scope it
-- cannot be frozen yet. This can happen in a package nested within
-- another, when freezing an expression function whose profile
-- depends on a type in some outer scope. Those types will be frozen
-- at a later time in the enclosing unit.
------------------------------
-- Has_Incomplete_Component --
------------------------------
function Has_Incomplete_Component (T : Entity_Id) return Boolean is
Comp : Entity_Id;
Comp_Typ : Entity_Id;
begin
if Nkind (N) /= N_Subprogram_Body
or else not Was_Expression_Function (N)
then
return False;
elsif In_Instance then
return False;
elsif Is_Record_Type (T) then
Comp := First_Entity (T);
while Present (Comp) loop
Comp_Typ := Etype (Comp);
if Ekind_In (Comp, E_Component, E_Discriminant)
and then Is_Private_Type (Comp_Typ)
and then No (Full_View (Comp_Typ))
and then In_Open_Scopes (Scope (Comp_Typ))
and then Scope (Comp_Typ) /= Current_Scope
then
return True;
end if;
Comp := Next_Entity (Comp);
end loop;
return False;
elsif Is_Array_Type (T) then
Comp_Typ := Component_Type (T);
return
Is_Private_Type (Comp_Typ)
and then No (Full_View (Comp_Typ))
and then In_Open_Scopes (Scope (Comp_Typ))
and then Scope (Comp_Typ) /= Current_Scope;
else
return False;
end if;
end Has_Incomplete_Component;
-- Local variables
F_Type : Entity_Id;
R_Type : Entity_Id;
Warn_Node : Node_Id;
-- Start of processing for Freeze_Profile
begin
-- Loop through formals
......@@ -3508,12 +3446,6 @@ package body Freeze is
Set_Etype (Formal, F_Type);
end if;
if Has_Incomplete_Component (F_Type) then
Set_Is_Frozen (E, False);
Result := No_List;
return False;
end if;
if not From_Limited_With (F_Type) then
Freeze_And_Append (F_Type, N, Result);
end if;
......@@ -8302,7 +8234,9 @@ package body Freeze is
-- that we know the convention.
if not Has_Foreign_Convention (E) then
Create_Extra_Formals (E);
if No (Extra_Formals (E)) then
Create_Extra_Formals (E);
end if;
Set_Mechanisms (E);
-- If this is convention Ada and a Valued_Procedure, that's odd
......
......@@ -728,11 +728,9 @@ package body Sem_Ch6 is
Insert_After (Last (Decls), New_Body);
-- Preanalyze the expression for name capture, except in an
-- instance, where this has been done during generic analysis,
-- and will be redone when analyzing the body.
-- Preanalyze the expression if not already done above
if not In_Instance then
if not Inside_A_Generic then
Push_Scope (Def_Id);
Install_Formals (Def_Id);
Preanalyze_Spec_Expression (Expr, Typ);
......@@ -2367,6 +2365,7 @@ package body Sem_Ch6 is
Desig_View : Entity_Id := Empty;
Exch_Views : Elist_Id := No_Elist;
HSS : Node_Id;
Mask_Types : Elist_Id := No_Elist;
Prot_Typ : Entity_Id := Empty;
Spec_Decl : Node_Id := Empty;
Spec_Id : Entity_Id;
......@@ -2442,6 +2441,12 @@ package body Sem_Ch6 is
-- Determine whether subprogram Subp_Id is a primitive of a concurrent
-- type that implements an interface and has a private view.
function Mask_Unfrozen_Types (Spec_Id : Entity_Id) return Elist_Id;
-- N is the body generated for an expression function that is not a
-- completion and Spec_Id the defining entity of its spec. Mark all
-- the not-yet-frozen types referenced by the simple return statement
-- of the function as formally frozen.
procedure Restore_Limited_Views (Restore_List : Elist_Id);
-- Undo the transformation done by Exchange_Limited_Views.
......@@ -2452,6 +2457,9 @@ package body Sem_Ch6 is
-- of an entity, we mark the entity as set in source to suppress any
-- warning on the stylized use of function stubs with a dummy return.
procedure Unmask_Unfrozen_Types (Unmask_List : Elist_Id);
-- Undo the transformation done by Mask_Unfrozen_Types
procedure Verify_Overriding_Indicator;
-- If there was a previous spec, the entity has been entered in the
-- current scope previously. If the body itself carries an overriding
......@@ -3195,6 +3203,73 @@ package body Sem_Ch6 is
return False;
end Is_Private_Concurrent_Primitive;
-------------------------
-- Mask_Unfrozen_Types --
-------------------------
function Mask_Unfrozen_Types (Spec_Id : Entity_Id) return Elist_Id is
Result : Elist_Id := No_Elist;
function Mask_Type_Refs (Node : Node_Id) return Traverse_Result;
-- Mask all types referenced in the subtree rooted at Node
--------------------
-- Mask_Type_Refs --
--------------------
function Mask_Type_Refs (Node : Node_Id) return Traverse_Result is
procedure Mask_Type (Typ : Entity_Id);
---------------
-- Mask_Type --
---------------
procedure Mask_Type (Typ : Entity_Id) is
begin
-- Skip Itypes created by the preanalysis
if Is_Itype (Typ)
and then Scope_Within_Or_Same (Scope (Typ), Spec_Id)
then
return;
end if;
if not Is_Frozen (Typ) then
Set_Is_Frozen (Typ);
Append_New_Elmt (Typ, Result);
end if;
end Mask_Type;
begin
if Is_Entity_Name (Node) and then Present (Entity (Node)) then
Mask_Type (Etype (Entity (Node)));
if Ekind_In (Entity (Node), E_Component, E_Discriminant) then
Mask_Type (Scope (Entity (Node)));
end if;
elsif Nkind_In (Node, N_Aggregate, N_Null, N_Type_Conversion)
and then Present (Etype (Node))
then
Mask_Type (Etype (Node));
end if;
return OK;
end Mask_Type_Refs;
procedure Mask_References is new Traverse_Proc (Mask_Type_Refs);
Return_Stmt : constant Node_Id :=
First (Statements (Handled_Statement_Sequence (N)));
begin
pragma Assert (Nkind (Return_Stmt) = N_Simple_Return_Statement);
Mask_References (Expression (Return_Stmt));
return Result;
end Mask_Unfrozen_Types;
---------------------------
-- Restore_Limited_Views --
---------------------------
......@@ -3236,6 +3311,20 @@ package body Sem_Ch6 is
end if;
end Set_Trivial_Subprogram;
---------------------------
-- Unmask_Unfrozen_Types --
---------------------------
procedure Unmask_Unfrozen_Types (Unmask_List : Elist_Id) is
Elmt : Elmt_Id := First_Elmt (Unmask_List);
begin
while Present (Elmt) loop
Set_Is_Frozen (Node (Elmt), False);
Next_Elmt (Elmt);
end loop;
end Unmask_Unfrozen_Types;
---------------------------------
-- Verify_Overriding_Indicator --
---------------------------------
......@@ -3610,8 +3699,22 @@ package body Sem_Ch6 is
or else (Operating_Mode = Check_Semantics
and then Serious_Errors_Detected = 0))
then
Set_Has_Delayed_Freeze (Spec_Id);
Freeze_Before (N, Spec_Id);
-- The body generated for an expression function that is not a
-- completion is a freeze point neither for the profile nor for
-- anything else. That's why, in order to prevent any freezing
-- during analysis, we need to mask types declared outside the
-- expression that are not yet frozen.
if Nkind (N) = N_Subprogram_Body
and then Was_Expression_Function (N)
and then not Has_Completion (Spec_Id)
then
Set_Is_Frozen (Spec_Id);
Mask_Types := Mask_Unfrozen_Types (Spec_Id);
else
Set_Has_Delayed_Freeze (Spec_Id);
Freeze_Before (N, Spec_Id);
end if;
end if;
end if;
......@@ -4455,6 +4558,10 @@ package body Sem_Ch6 is
Restore_Limited_Views (Exch_Views);
end if;
if Mask_Types /= No_Elist then
Unmask_Unfrozen_Types (Mask_Types);
end if;
if Present (Desig_View) then
Set_Directly_Designated_Type (Etype (Spec_Id), Desig_View);
end if;
......
......@@ -11450,7 +11450,7 @@ package body Sem_Res is
begin
-- Do not perform this transformation within a pre/postcondition,
-- because the expression will be re-analyzed, and the transformation
-- because the expression will be reanalyzed, and the transformation
-- might affect the visibility of the operator, e.g. in an instance.
-- Note that fully analyzed and expanded pre/postconditions appear as
-- pragma Check equivalents.
......@@ -11459,6 +11459,22 @@ package body Sem_Res is
return;
end if;
-- Likewise when an expression function is being preanalyzed, since the
-- expression will be reanalyzed as part of the generated body.
if In_Spec_Expression then
declare
S : constant Entity_Id := Current_Scope_No_Loops;
begin
if Ekind (S) = E_Function
and then Nkind (Original_Node (Unit_Declaration_Node (S)))
= N_Expression_Function
then
return;
end if;
end;
end if;
-- Rewrite the operator node using the real operator, not its renaming.
-- Exclude user-defined intrinsic operations of the same name, which are
-- treated separately and rewritten as calls.
......
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