Skip to content

Commit f27c002

Browse files
authored
Further simplification of the IR tree (NomicFoundation#1473)
This PR adds more simplifications to the IR tree, reducing the number of non-terminal node types from 220 to 166. These include: - Constant non-public `StateVariableDefinition`s are transmuted into `ConstantDefinition`s (public vars generate a getter, which would trade the reduced complexity with the binder passes) - File import clauses are unified under `PathImport`; symbol deconstruction imports are still a separate node type - `VariableDeclarationStatement` are used for tuple declarations as well, so any local variable is now unified under the same type - Removed redundant `VariableDeclarationType` for an optional `TypeName` - Parameters (function, error, event) are unified under a single `Parameter` node type, which now has an `indexed` boolean property to accommodate event parameters - `Parameter` is also used for the mapping key and value types as well
1 parent 7b32158 commit f27c002

10 files changed

Lines changed: 635 additions & 936 deletions

File tree

crates/codegen/generator/src/ir/builders/ir2_flat_contracts.rs

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ pub(super) fn build_from(structured_ast_model: &IrModel) -> ModelWithTransformer
77
unify_function_types(&mut mutator);
88
flatten_function_attributes(&mut mutator);
99
flatten_state_variable_attributes(&mut mutator);
10+
transmute_constant_state_variables(&mut mutator);
1011
collapse_redundant_node_types(&mut mutator);
1112
simplify_string_literals(&mut mutator);
13+
simplify_imports(&mut mutator);
14+
simplify_variable_declarations(&mut mutator);
15+
simplify_parameters(&mut mutator);
16+
simplify_mapping_type_parameters(&mut mutator);
1217

1318
mutator.into()
1419
}
@@ -138,13 +143,17 @@ fn flatten_function_attributes(mutator: &mut IrModelMutator) {
138143
}
139144

140145
fn flatten_state_variable_attributes(mutator: &mut IrModelMutator) {
141-
// Function visibility, computed from a subset of the attributes
146+
// State variable visibility, computed from a subset of the attributes
142147
mutator.add_enum_type(
143148
"StateVariableVisibility",
144149
&["Public", "Private", "Internal"],
145150
);
146151

147-
// Function mutability, computed from a subset of the attributes
152+
// State variable mutability, computed from a subset of the attributes.
153+
// NB. Even though most constant declarations are transformed into
154+
// `ConstantDefinition`, those with `public` visibility need to generate a
155+
// getter and thus we still need to represent them using a
156+
// `StateVariableDefinition`.
148157
mutator.add_enum_type(
149158
"StateVariableMutability",
150159
&["Mutable", "Constant", "Immutable", "Transient"],
@@ -174,14 +183,35 @@ fn flatten_state_variable_attributes(mutator: &mut IrModelMutator) {
174183
mutator.remove_type("StateVariableAttribute");
175184
}
176185

186+
fn transmute_constant_state_variables(mutator: &mut IrModelMutator) {
187+
// State variables that are marked constant and are not public will be
188+
// transformed into `ConstantDefinition` (ie. the type used for top-level
189+
// constant definitions). Public constant state variables *cannot* be
190+
// transformed because they generate a getter, so it makes more sense to
191+
// keep them as `StateVariableDefinition`
192+
mutator.add_choice_variant("ContractMember", "ConstantDefinition");
193+
194+
// Modify `ConstantDefinition` to accomodate for constant state variables
195+
mutator.add_sequence_field(
196+
"ConstantDefinition",
197+
"visibility",
198+
"StateVariableVisibility",
199+
true,
200+
);
201+
202+
// ...and make the value optional because state variables may not define it
203+
// NOTE: this is not valid Solidity, but we still want to support the
204+
// representation until we have robust validation
205+
mutator.remove_sequence_field("ConstantDefinition", "value");
206+
mutator.add_sequence_field("ConstantDefinition", "value", "Expression", true);
207+
}
208+
177209
fn collapse_redundant_node_types(mutator: &mut IrModelMutator) {
178210
// Collapse redundant node types
179211
mutator.collapse_sequence("ParametersDeclaration");
180212
mutator.collapse_sequence("ReturnsDeclaration");
181213
mutator.collapse_sequence("YulParametersDeclaration");
182214
mutator.collapse_sequence("YulReturnsDeclaration");
183-
mutator.collapse_sequence("EventParametersDeclaration");
184-
mutator.collapse_sequence("ErrorParametersDeclaration");
185215
mutator.collapse_sequence("ImportAlias");
186216
mutator.collapse_sequence("ElseBranch");
187217
mutator.collapse_sequence("UsingAlias");
@@ -244,3 +274,78 @@ fn simplify_string_literals(mutator: &mut IrModelMutator) {
244274
mutator.add_sequence_field("AssemblyStatement", "flags", "AssemblyFlags", false);
245275
mutator.add_sequence_field("AssemblyStatement", "label", "StringLiteral", true);
246276
}
277+
278+
fn simplify_imports(mutator: &mut IrModelMutator) {
279+
// Collapse `ImportDirective` which is only a container for the `ImportClause`
280+
mutator.collapse_sequence("ImportDirective");
281+
// Remove `NamedImport`, since it can be converted to the equivalent `PathImport`
282+
mutator.remove_type("NamedImport");
283+
// TODO: collapsing `ImportDirective` means that the `ImportClause` gets
284+
// lifted to the `SourceUnitMember` enum and its name is inconsistent with
285+
// the rest of the variants (eg. `PragmaDirective` or `UsingDirective`). We
286+
// want to either rename `ImportClause` to `ImportDirective` (this may lead
287+
// to confusion due to the difference against the CST) or find a new better
288+
// name.
289+
}
290+
291+
fn simplify_variable_declarations(mutator: &mut IrModelMutator) {
292+
// Collapse the `VariableDeclarationType` into the parent `VariableDeclarationStatement`
293+
mutator.remove_type("VariableDeclarationType");
294+
mutator.add_sequence_field(
295+
"VariableDeclarationStatement",
296+
"type_name",
297+
"TypeName",
298+
true,
299+
);
300+
301+
// Re-use `VariableDeclarationStatement` for variable declarations in tuple
302+
// deconstruction expressions. Remove `TupleMember` first.
303+
mutator.remove_type("UntypedTupleMember");
304+
mutator.remove_type("TypedTupleMember");
305+
mutator.remove_type("TupleMember");
306+
mutator.remove_type("TupleDeconstructionElements");
307+
mutator.remove_type("TupleDeconstructionElement");
308+
309+
// Create a `TupleDeconstructionMember` initially as an enum so that the
310+
// `None` variant has no child
311+
mutator.add_enum_type("TupleDeconstructionMember", &["None"]);
312+
mutator.add_choice_variant("TupleDeconstructionMember", "Identifier");
313+
mutator.add_choice_variant("TupleDeconstructionMember", "VariableDeclarationStatement");
314+
mutator.add_collection_type("TupleDeconstructionMembers", "TupleDeconstructionMember");
315+
mutator.add_sequence_field(
316+
"TupleDeconstructionStatement",
317+
"members",
318+
"TupleDeconstructionMembers",
319+
false,
320+
);
321+
// This refactor also means we don't need the `var_keyword` field in the
322+
// `TupleDeconstructionStatement` anymore
323+
mutator.remove_sequence_field("TupleDeconstructionStatement", "var_keyword");
324+
}
325+
326+
fn simplify_parameters(mutator: &mut IrModelMutator) {
327+
// Replace `EventParameter` and `ErrorParameter` with `Parameter`. This
328+
// requires adding an `indexed` attribute (required for event parameters).
329+
mutator.add_sequence_field("Parameter", "indexed", "IndexedKeyword", true);
330+
331+
mutator.remove_type("EventParametersDeclaration");
332+
mutator.remove_type("EventParameters");
333+
mutator.remove_type("EventParameter");
334+
mutator.add_sequence_field("EventDefinition", "parameters", "Parameters", false);
335+
336+
mutator.remove_type("ErrorParametersDeclaration");
337+
mutator.remove_type("ErrorParameters");
338+
mutator.remove_type("ErrorParameter");
339+
mutator.add_sequence_field("ErrorDefinition", "parameters", "Parameters", false);
340+
}
341+
342+
fn simplify_mapping_type_parameters(mutator: &mut IrModelMutator) {
343+
// Replace `MappingKey` and `MappingValue` with regular `Parameter` types.
344+
// `MappingKeyType` is a subset of `TypeName` and can be removed as well.
345+
mutator.remove_type("MappingKeyType");
346+
mutator.remove_type("MappingKey");
347+
mutator.remove_type("MappingValue");
348+
349+
mutator.add_sequence_field("MappingType", "key_type", "Parameter", false);
350+
mutator.add_sequence_field("MappingType", "value_type", "Parameter", false);
351+
}

crates/codegen/generator/src/ir/mutator.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ impl IrModelMutator {
366366
!replace_field.is_optional,
367367
"Cannot collapse sequence {sequence_id} of an optional field"
368368
);
369+
let replaced_type = self.find_node_type(&sequence_id.into());
369370

370371
// Iterate remaining sequences and replace any fields referencing the
371372
// removed type by the target type
@@ -377,6 +378,20 @@ impl IrModelMutator {
377378
}
378379
}
379380

381+
// Iterate choice types, remove type to be collapsed and add the
382+
// replacement variant instead
383+
// TODO: the transformer for this case is not generated automatically,
384+
// but if we change the structure of `MutatedChoice` we could accomodate it
385+
for (_, choice) in &mut self.choices {
386+
if choice.variants.contains(&replaced_type) {
387+
choice.has_removed_variants = true;
388+
choice.variants.retain(|item| *item != identifier);
389+
choice
390+
.added_variants
391+
.push(replace_field.target_type.clone());
392+
}
393+
}
394+
380395
// Determine the target type; the type of the single field may be
381396
// already collapsed, so we need to use it in that case
382397
let target_type = if let Some(collapsed) = self

0 commit comments

Comments
 (0)