| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G E N _ I L . G E N -- |
| -- -- |
| -- S p e c -- |
| -- -- |
| -- Copyright (C) 2020-2022, 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. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| -- "Language design is library design and library design is language design". |
| -- -- Bjarne Stroustrup |
| |
| -- This package provides a "little language" for defining type hierarchies, |
| -- which we call "Gen_IL.Gen". In particular, it is used to describe the type |
| -- hierarchies rooted at Node_Id and Entity_Id in the intermediate language |
| -- used by GNAT. |
| |
| -- The type hierarchy is a strict hierarchy (treeish, no multiple |
| -- inheritance). We have "abstract" and "concrete" types. Each type has a |
| -- "parent", except for the root type (Node_Id or Entity_Id). All leaf types |
| -- in the hierarchy are concrete; all nonleaf types (including the two root |
| -- types) are abstract. One can create instances of concrete, but not |
| -- abstract, types. |
| -- |
| -- Descendants of Node_Id/Node_Kind are node types, and descendants of |
| -- Entity_Id/Entity_Kind are entity types. |
| -- |
| -- Types have "fields". Each type inherits all the fields from its parent, and |
| -- may add new ones. A node field can be marked "syntactic"; entity fields are |
| -- never syntactic. A nonsyntactic field is "semantic". |
| -- |
| -- If a field is syntactic, then the constructors in Nmake take a parameter to |
| -- initialize that field. In addition, the tree-traversal routines in Atree |
| -- (Traverse_Func and Traverse_Proc) traverse syntactic fields that are of |
| -- type Node_Id (or subtypes of Node_Id) or List_Id. Finally, (with some |
| -- exceptions documented in the body) the setter for a syntactic node or list |
| -- field "Set_F (N, Val)" will set the Parent of Val to N, unless Val is Empty |
| -- or Error[_List]. |
| -- |
| -- Note that the same field can be syntactic in some node types but semantic |
| -- in other node types. This is an added complexity that we might want to |
| -- eliminate someday. We shouldn't add any new such cases. |
| -- |
| -- A "program" written in the Gen_IL.Gen language consists of calls to the |
| -- "Create_..." routines below, followed by a call to Compile, also below. In |
| -- order to understand what's going on, you need to look not only at the |
| -- Gen_IL.Gen "code", but at the output of the compiler -- at least, look at |
| -- the specs of Sinfo.Nodes and Einfo.Entities, because GNAT invokes those |
| -- directly. It's not like a normal language where you don't usually have to |
| -- look at the generated machine code. |
| -- |
| -- Thus, the Gen_IL.Gen code is really Ada code, and when you run it as an Ada |
| -- program, it generates the above-mentioned files. The program is somewhat |
| -- unusual in that it has no input. Everything it needs to generate code is |
| -- embodied in it. |
| |
| -- Why don't we just use a variant record, instead of inventing a wheel? |
| -- Or a hierarchy of tagged types? |
| -- |
| -- The key feature that Ada's variant records and tagged types lack, and that |
| -- this little language has, is that if two types have a field with the same |
| -- name, then those are the same field, even though they weren't inherited |
| -- from a common ancestor. Such fields are required to have the same type, the |
| -- same default value, and the same extra precondition. |
| |
| with Gen_IL.Types; use Gen_IL.Types; |
| pragma Warnings (Off); |
| with Gen_IL.Fields; use Gen_IL.Fields; -- for children |
| pragma Warnings (On); |
| with Gen_IL.Internals; use Gen_IL.Internals; |
| use Gen_IL.Internals.Type_Vectors; |
| use Gen_IL.Internals.Field_Vectors; |
| |
| package Gen_IL.Gen is |
| |
| procedure Create_Root_Node_Type |
| (T : Abstract_Node; |
| Fields : Field_Sequence := No_Fields) |
| with Pre => T = Node_Kind; |
| -- Create the root node type (Node_Kind), which is an abstract type |
| |
| procedure Create_Abstract_Node_Type |
| (T : Abstract_Node; Parent : Abstract_Type; |
| Fields : Field_Sequence := No_Fields); |
| -- Create an abstract node type (other than the root node type) |
| |
| procedure Create_Concrete_Node_Type |
| (T : Concrete_Node; Parent : Abstract_Type; |
| Fields : Field_Sequence := No_Fields; |
| Nmake_Assert : String := ""); |
| -- Create a concrete node type. Every node is an instance of a concrete |
| -- node type. Nmake_Assert is an assertion to put in the Make_... function |
| -- in the generated Nmake package. It should be a String that represents a |
| -- Boolean expression. |
| |
| procedure Create_Root_Entity_Type |
| (T : Abstract_Entity; |
| Fields : Field_Sequence := No_Fields) |
| with Pre => T = Entity_Kind; |
| -- Create the root entity type (Entity_Kind), which is an abstract type |
| |
| procedure Create_Abstract_Entity_Type |
| (T : Abstract_Entity; Parent : Abstract_Type; |
| Fields : Field_Sequence := No_Fields); |
| -- Create an abstract entity type (other than the root entity type) |
| |
| procedure Create_Concrete_Entity_Type |
| (T : Concrete_Entity; Parent : Abstract_Type; |
| Fields : Field_Sequence := No_Fields); |
| -- Create a concrete entity type. Every entity is an instance of a concrete |
| -- entity type. |
| |
| function Create_Syntactic_Field |
| (Field : Node_Field; |
| Field_Type : Type_Enum; |
| Default_Value : Field_Default_Value := No_Default; |
| Pre, Pre_Get, Pre_Set : String := "") return Field_Desc; |
| -- Create a syntactic field of a node type. Entities do not have syntactic |
| -- fields. |
| |
| function Create_Semantic_Field |
| (Field : Field_Enum; |
| Field_Type : Type_Enum; |
| Type_Only : Type_Only_Enum := No_Type_Only; |
| Pre, Pre_Get, Pre_Set : String := "") return Field_Desc; |
| -- Create a semantic field of a node or entity type |
| |
| -- Create_Syntactic_Field is used for syntactic fields of nodes. The order |
| -- of calls to Create_Syntactic_Field determines the order of the formal |
| -- parameters of the Make_... functions in Nmake. |
| -- |
| -- Create_Semantic_Field is used for semantic fields of nodes, and all |
| -- fields of entities are considered semantic. The order of calls doesn't |
| -- make any difference. |
| -- |
| -- Field_Type is the type of the field. Default_Value is the default value |
| -- for the parameter of the Make_... function in Nmake; this is effective |
| -- only for syntactic fields. Flag fields of syntactic nodes always have a |
| -- default value, which is False unless specified as Default_True. Pre is |
| -- an additional precondition for the field getter and setter, in addition |
| -- to the precondition that asserts that the type has that field. It should |
| -- be a String that represents a Boolean expression. Pre_Get and Pre_Set |
| -- are similar to Pre, but for the getter or setter only, respectively. |
| -- |
| -- If multiple calls to these occur for the same Field but different types, |
| -- the Field_Type, Pre, Pre_Get, and Pre_Set must match. Default_Value |
| -- should match for syntactic fields. See the declaration of Type_Only_Enum |
| -- for Type_Only. |
| -- |
| -- (The matching Default_Value requirement is a simplification from the |
| -- earlier hand-written version.) |
| |
| -- When adding new node or entity kinds, or adding new fields, all back |
| -- ends must be made aware of the changes. In addition, the documentation |
| -- in Sinfo or Einfo needs to be updated. |
| |
| -- To add a new node or entity type, add it to the enumeration type in |
| -- Gen_IL.Types, taking care that it is in the approprate range |
| -- (Abstract_Node, Abstract_Entity, Concrete_Node, or Concrete_Entity). |
| -- Then add a call to one of the above type-creation procedures to |
| -- Gen_IL.Gen.Gen_Nodes or Gen_IL.Gen.Gen_Entities. |
| -- |
| -- To add a new field to a type, add it to the enumeration type in |
| -- Gen_IL.Fields in the appropriate range. Then add a call to one of |
| -- the above field-creation procedures to Gen_IL.Gen.Gen_Nodes or |
| -- Gen_IL.Gen.Gen_Entities. |
| -- |
| -- If a type or field name does not follow the usual Mixed_Case convention, |
| -- such as "SPARK_Pragma", then you have to add a special case to one of |
| -- the Image functions in Gen_IL.Internals and in Treepr. |
| |
| -- Forward references are not allowed. So if you say: |
| -- |
| -- Create..._Type (..., Parent => P); |
| -- |
| -- then Create..._Type must have already been called to create P. |
| -- |
| -- Likewise, if you say: |
| -- |
| -- Create..._Field (T, F, Field_Type, ...); |
| -- |
| -- then Create..._Type must have already been called to create T and |
| -- (if it's a node or entity type) to create Field_Type. |
| -- |
| -- To delete a node or entity type, delete it from Gen_IL.Types, update the |
| -- subranges in Gen_IL.Internals if necessary, and delete all occurrences |
| -- from Gen_IL.Gen.Gen_Entities. To delete a field, delete it from |
| -- Gen_IL.Fields, and delete all occurrences from Gen_IL.Gen.Gen_Entities. |
| |
| -- If a field is not set, it is initialized by default to whatever value is |
| -- represented by all-zero bits, with some exceptions. This means Flags are |
| -- initialized to False, Node_Ids and List_Ids are initialized to Empty, |
| -- and enumeration fields are initialized to 'First of the type (assuming |
| -- there is no representation clause). |
| -- |
| -- Elists default to No_Elist. |
| -- |
| -- Fields of type Uint (but not its subtypes) are initialized to No_Uint. |
| -- Fields of subtypes Valid_Uint, Unat, Upos, Nonzero_Uint, and Ureal have |
| -- no default; it is an error to call a getter before calling the setter. |
| -- Likewise, other types whose range does not include zero have no default |
| -- (see package Types for the ranges). |
| -- |
| -- If a node is created by a function in Nmake, then the defaults are |
| -- different from what is specified above. The parameters of Make_... |
| -- functions can have defaults specified; see Create_Syntactic_Field. |
| |
| procedure Create_Node_Union_Type |
| (T : Abstract_Node; Children : Type_Array); |
| procedure Create_Entity_Union_Type |
| (T : Abstract_Entity; Children : Type_Array); |
| -- Create a "union" type that is the union of the Children. This is used |
| -- for nonhierachical types. This is the opposite of the normal "object |
| -- oriented" routines above, which create child types based on existing |
| -- parents. Here we are creating parent types based on existing child |
| -- types. A union type is considered to be an abstract type because it has |
| -- multiple children. We do not allow union types to have their own fields, |
| -- because that would introduce the well-known complexity of multiple |
| -- inheritance. That restriction could be relaxed, but for now, union types |
| -- are mainly for allowing things like "Pre => X in Some_Union_Type". |
| |
| Illegal : exception; |
| -- Exception raised when Gen_IL code (in particular in Gen_Nodes and |
| -- Gen_Entities) is illegal. We don't try elaborate error recovery, but |
| -- hopefully the exception message will indicate what's wrong. You might |
| -- have to go in the debugger to see which line it's complaining about. |
| |
| procedure Compile; |
| |
| private |
| |
| function Sy |
| (Field : Node_Field; |
| Field_Type : Type_Enum; |
| Default_Value : Field_Default_Value := No_Default; |
| Pre, Pre_Get, Pre_Set : String := "") return Field_Sequence; |
| function Sm |
| (Field : Field_Enum; |
| Field_Type : Type_Enum; |
| Type_Only : Type_Only_Enum := No_Type_Only; |
| Pre, Pre_Get, Pre_Set : String := "") return Field_Sequence; |
| -- The above functions return Field_Sequence. This is a trick to get around |
| -- the fact that Ada doesn't allow singleton positional aggregates. It |
| -- allows us to write things like: |
| -- |
| -- Cc (N_Empty, Node_Kind, |
| -- (Sy (Chars, Name_Id, Default_No_Name))); |
| -- |
| -- where that thing pretending to be an aggregate is really a parenthesized |
| -- expression. See Gen_Nodes for documentation of the functions these are |
| -- standing in for. |
| |
| end Gen_IL.Gen; |