Skip to content

Commit 7b32158

Browse files
authored
Introduce SemanticAnalysis to gather the IR, binding and typing (NomicFoundation#1476)
The new `SemanticAnalysis` object is the composition of the `Binder`, `TypeRegistry` and the IR trees for all the files in the `CompilationUnit`. Replaces the ad-hoc `Output` structure from the last pass in the backend pipeline. It's created from the `CompilationUnit` in a similar way to how the current `BindingGraph`. NOTE: this is only the introduction of the type to provide a foundation to build on. The API of `SemanticAnalysis` is not designed yet.
1 parent a3b8c14 commit 7b32158

32 files changed

Lines changed: 378 additions & 324 deletions

File tree

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
11
#![allow(missing_docs)]
22

3-
use crate::compilation::CompilationUnit;
4-
53
pub mod binder;
64
pub mod built_ins;
75
pub mod ir;
86
pub mod passes;
7+
pub mod semantic;
98
pub mod types;
109

11-
pub type BinderOutput = passes::p5_resolve_references::Output;
12-
13-
pub fn build_binder_output(compilation_unit: CompilationUnit) -> BinderOutput {
14-
let data = passes::p0_build_ast::run(compilation_unit);
15-
let data = passes::p1_flatten_contracts::run(data);
16-
let data = passes::p2_collect_definitions::run(data);
17-
let data = passes::p3_linearise_contracts::run(data);
18-
let data = passes::p4_type_definitions::run(data);
19-
passes::p5_resolve_references::run(data)
20-
}
10+
pub use semantic::{SemanticAnalysis, SemanticFile};
Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,6 @@
1-
use std::collections::HashMap;
2-
31
use crate::backend::ir::ir1_structured_ast::{builder, SourceUnit};
4-
use crate::compilation::CompilationUnit;
5-
6-
pub struct Output {
7-
pub compilation_unit: CompilationUnit,
8-
pub files: HashMap<String, SourceUnit>,
9-
}
2+
use crate::compilation::File;
103

11-
pub fn run(input: CompilationUnit) -> Output {
12-
let mut files = HashMap::new();
13-
for file in &input.files() {
14-
if let Some(source_unit) = builder::build_source_unit(file.tree()) {
15-
files.insert(file.id().into(), source_unit);
16-
}
17-
}
18-
Output {
19-
compilation_unit: input,
20-
files,
21-
}
4+
pub fn run_file(file: &File) -> Option<SourceUnit> {
5+
builder::build_source_unit(file.tree())
226
}

crates/solidity/outputs/cargo/crate/src/backend/passes/p1_flatten_contracts.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,21 @@
1-
use std::collections::HashMap;
21
use std::rc::Rc;
32

43
use semver::Version;
54

6-
use super::p0_build_ast::Output as Input;
75
use crate::backend::ir::ir2_flat_contracts::transformer::Transformer;
8-
use crate::backend::ir::ir2_flat_contracts::{self as output, input, SourceUnit};
9-
use crate::compilation::CompilationUnit;
6+
use crate::backend::ir::ir2_flat_contracts::{self as output, input};
107
use crate::cst::TerminalNode;
118
use crate::utils::versions::VERSION_0_5_0;
129

13-
pub struct Output {
14-
pub compilation_unit: CompilationUnit,
15-
pub files: HashMap<String, SourceUnit>,
16-
}
17-
18-
/// This pass is reserved to make ergonomic changes to the AST in order to make
19-
/// it easier to use. For now, it will only flatten contract specifiers:
20-
/// inheritance and storage layout specifiers. In the future, more
21-
/// transformations will be added.
22-
pub fn run(input: Input) -> Output {
10+
/// This pass makes ergonomic changes to the AST in order to make it easier to
11+
/// use. It includes changes such as removing redundant intermediate nodes,
12+
/// unifying similar node types and computing visibility/mutability from
13+
/// collections of attributes.
14+
pub fn run_file(language_version: &Version, source_unit: &input::SourceUnit) -> output::SourceUnit {
2315
let mut pass = Pass {
24-
language_version: input.compilation_unit.language_version().clone(),
16+
language_version: language_version.clone(),
2517
};
26-
let files = input
27-
.files
28-
.iter()
29-
.map(|(file_id, source_unit)| (file_id.clone(), pass.transform_source_unit(source_unit)))
30-
.collect();
31-
let compilation_unit = input.compilation_unit;
32-
Output {
33-
compilation_unit,
34-
files,
35-
}
18+
pass.transform_source_unit(source_unit)
3619
}
3720

3821
struct Pass {

crates/solidity/outputs/cargo/crate/src/backend/passes/p2_collect_definitions.rs

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,28 @@
1-
use std::collections::HashMap;
21
use std::rc::Rc;
32

43
use semver::Version;
54

6-
use super::p1_flatten_contracts::Output as Input;
75
use crate::backend::binder::{Binder, Definition, FileScope, ParametersScope, Scope, ScopeId};
86
use crate::backend::ir::ir2_flat_contracts::visitor::Visitor;
97
use crate::backend::ir::ir2_flat_contracts::{self as input_ir};
10-
use crate::compilation::{CompilationUnit, File};
8+
use crate::backend::semantic::SemanticAnalysis;
9+
use crate::compilation::File;
1110
use crate::cst::{NodeId, TerminalNode};
1211
use crate::utils::versions::VERSION_0_5_0;
1312

14-
pub struct Output {
15-
pub compilation_unit: CompilationUnit,
16-
pub files: HashMap<String, input_ir::SourceUnit>,
17-
pub binder: Binder,
18-
}
19-
2013
/// In this pass all definitions are collected with their naming identifiers.
2114
/// Also lexical (and other kinds of) scopes are identified and linked together,
2215
/// and definitions are registered into them for later lookup. The pass
2316
/// instantiates a `Binder` object which will store all this information as well
2417
/// as references and typing information for the nodes, to be resolved in later
2518
/// passes.
26-
pub fn run(input: Input) -> Output {
27-
let files = input.files;
28-
let compilation_unit = input.compilation_unit;
29-
let mut pass = Pass::new(compilation_unit.language_version());
30-
for (file_id, source_unit) in &files {
31-
let file = compilation_unit.file(file_id).unwrap();
32-
pass.visit_file(file, source_unit);
33-
}
34-
let binder = pass.binder;
35-
36-
Output {
37-
compilation_unit,
38-
files,
39-
binder,
19+
pub fn run(semantic_analysis: &mut SemanticAnalysis) {
20+
let mut pass = Pass::new(
21+
semantic_analysis.language_version().clone(),
22+
&mut semantic_analysis.binder,
23+
);
24+
for semantic_file in semantic_analysis.files.values() {
25+
pass.visit_file(semantic_file.file(), semantic_file.ir_root());
4026
}
4127
}
4228

@@ -48,27 +34,27 @@ struct ScopeFrame {
4834
lexical_scope_id: ScopeId,
4935
}
5036

51-
struct Pass {
37+
struct Pass<'a> {
5238
language_version: Version,
5339
current_file: Option<Rc<File>>, // needed to resolve imports on the file
5440
scope_stack: Vec<ScopeFrame>,
55-
binder: Binder,
41+
binder: &'a mut Binder,
5642
}
5743

58-
impl Pass {
59-
fn new(language_version: &Version) -> Self {
44+
impl<'a> Pass<'a> {
45+
fn new(language_version: Version, binder: &'a mut Binder) -> Self {
6046
Self {
61-
language_version: language_version.clone(),
47+
language_version,
6248
current_file: None,
6349
scope_stack: Vec::new(),
64-
binder: Binder::new(),
50+
binder,
6551
}
6652
}
6753

68-
fn visit_file(&mut self, file: Rc<File>, source_unit: &input_ir::SourceUnit) {
54+
fn visit_file(&mut self, file: &Rc<File>, source_unit: &input_ir::SourceUnit) {
6955
assert!(self.current_file.is_none());
7056

71-
self.current_file = Some(file);
57+
self.current_file = Some(Rc::clone(file));
7258
input_ir::visitor::accept_source_unit(source_unit, self);
7359
self.current_file = None;
7460

@@ -233,7 +219,7 @@ impl Pass {
233219
}
234220
}
235221

236-
impl Visitor for Pass {
222+
impl Visitor for Pass<'_> {
237223
fn enter_source_unit(&mut self, node: &input_ir::SourceUnit) -> bool {
238224
let Some(current_file) = &self.current_file else {
239225
unreachable!("visiting SourceUnit without a current file being set");

crates/solidity/outputs/cargo/crate/src/backend/passes/p3_linearise_contracts/mod.rs

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,35 @@
11
use std::collections::HashMap;
22
use std::rc::Rc;
33

4-
use super::p2_collect_definitions::Output as Input;
54
use crate::backend::binder::{
65
Binder, ContractDefinition, Definition, ImportDefinition, InterfaceDefinition,
76
LibraryDefinition, Reference, Resolution, ScopeId,
87
};
98
use crate::backend::ir::ir2_flat_contracts::{self as input_ir};
10-
use crate::compilation::CompilationUnit;
9+
use crate::backend::semantic::SemanticAnalysis;
1110
use crate::cst::NodeId;
1211

1312
mod c3;
1413

15-
pub struct Output {
16-
pub compilation_unit: CompilationUnit,
17-
pub files: HashMap<String, input_ir::SourceUnit>,
18-
pub binder: Binder,
19-
}
20-
2114
/// In this pass we collect all bases of contracts and interfaces and then
2215
/// compute the linearisation for each of them.
23-
pub fn run(input: Input) -> Output {
24-
let files = input.files;
25-
let mut pass = Pass::new(input.compilation_unit, input.binder);
26-
for source_unit in files.values() {
27-
pass.visit_file_collect_bases(source_unit);
16+
pub fn run(semantic_analysis: &mut SemanticAnalysis) {
17+
let mut pass = Pass::new(&mut semantic_analysis.binder);
18+
for semantic_file in semantic_analysis.files.values() {
19+
pass.visit_file_collect_bases(semantic_file.ir_root());
2820
}
29-
for source_unit in files.values() {
30-
pass.visit_file_linearise_contracts(source_unit);
31-
}
32-
33-
let compilation_unit = pass.compilation_unit;
34-
let binder = pass.binder;
35-
Output {
36-
compilation_unit,
37-
files,
38-
binder,
21+
for semantic_file in semantic_analysis.files.values() {
22+
pass.visit_file_linearise_contracts(semantic_file.ir_root());
3923
}
4024
}
4125

42-
pub struct Pass {
43-
pub compilation_unit: CompilationUnit,
44-
pub binder: Binder,
26+
pub struct Pass<'a> {
27+
pub binder: &'a mut Binder,
4528
}
4629

47-
impl Pass {
48-
pub fn new(compilation_unit: CompilationUnit, binder: Binder) -> Self {
49-
Self {
50-
compilation_unit,
51-
binder,
52-
}
30+
impl<'a> Pass<'a> {
31+
pub fn new(binder: &'a mut Binder) -> Self {
32+
Self { binder }
5333
}
5434

5535
fn visit_file_collect_bases(&mut self, source_unit: &input_ir::SourceUnit) {

crates/solidity/outputs/cargo/crate/src/backend/passes/p4_type_definitions/mod.rs

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
1-
use std::collections::HashMap;
2-
31
use semver::Version;
42

5-
use super::p3_linearise_contracts::Output as Input;
63
use crate::backend::binder::{Binder, Definition, Scope, ScopeId};
74
use crate::backend::ir::ir2_flat_contracts::{self as input_ir};
5+
use crate::backend::semantic::SemanticAnalysis;
86
use crate::backend::types::{Type, TypeId, TypeRegistry};
9-
use crate::compilation::CompilationUnit;
107
use crate::cst::NodeId;
118

129
mod resolution;
1310
mod typing;
1411
mod visitor;
1512

16-
pub struct Output {
17-
pub compilation_unit: CompilationUnit,
18-
pub files: HashMap<String, input_ir::SourceUnit>,
19-
pub binder: Binder,
20-
pub types: TypeRegistry,
21-
}
22-
2313
/// This pass will determine and assign typing information to all the
2414
/// definitions collected in the earlier pass and as a result register most
2515
/// types used in the compilation unit, by instantiating a `TypeRegistry`
@@ -29,42 +19,35 @@ pub struct Output {
2919
/// Finally, public state variables will be assigned an equivalent getter
3020
/// function type. This happens after the main typing pass to ensure all types
3121
/// are already registered.
32-
pub fn run(input: Input) -> Output {
33-
let files = input.files;
34-
let compilation_unit = input.compilation_unit;
35-
let mut pass = Pass::new(input.binder, compilation_unit.language_version());
36-
for source_unit in files.values() {
37-
pass.visit_file(source_unit);
22+
pub fn run(semantic_analysis: &mut SemanticAnalysis) {
23+
let mut pass = Pass::new(
24+
semantic_analysis.language_version().clone(),
25+
&mut semantic_analysis.binder,
26+
&mut semantic_analysis.types,
27+
);
28+
for semantic_file in semantic_analysis.files.values() {
29+
pass.visit_file(semantic_file.ir_root());
3830
}
39-
for source_unit in files.values() {
40-
pass.visit_file_type_getters(source_unit);
41-
}
42-
let binder = pass.binder;
43-
let types = pass.types;
44-
45-
Output {
46-
compilation_unit,
47-
files,
48-
binder,
49-
types,
31+
for semantic_file in semantic_analysis.files.values() {
32+
pass.visit_file_type_getters(semantic_file.ir_root());
5033
}
5134
}
5235

53-
struct Pass {
36+
struct Pass<'a> {
5437
language_version: Version,
5538
scope_stack: Vec<ScopeId>,
56-
binder: Binder,
57-
types: TypeRegistry,
39+
binder: &'a mut Binder,
40+
types: &'a mut TypeRegistry,
5841
current_receiver_type: Option<TypeId>,
5942
}
6043

61-
impl Pass {
62-
fn new(binder: Binder, language_version: &Version) -> Self {
44+
impl<'a> Pass<'a> {
45+
fn new(language_version: Version, binder: &'a mut Binder, types: &'a mut TypeRegistry) -> Self {
6346
Self {
64-
language_version: language_version.clone(),
47+
language_version,
6548
scope_stack: Vec::new(),
6649
binder,
67-
types: TypeRegistry::new(language_version.clone()),
50+
types,
6851
current_receiver_type: None,
6952
}
7053
}

crates/solidity/outputs/cargo/crate/src/backend/passes/p4_type_definitions/resolution.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::backend::binder::{
88
use crate::backend::ir::ir2_flat_contracts::{self as input_ir};
99
use crate::backend::types::{DataLocation, FunctionType, Type, TypeId};
1010

11-
impl Pass {
11+
impl Pass<'_> {
1212
// Resolves an IdentifierPath. It starts resolution at the "contract" scope
1313
// level, or at the file level if there's no contract scope open. It will
1414
// follow through in contracts/intrefaces/libraries as well as imports and

crates/solidity/outputs/cargo/crate/src/backend/passes/p4_type_definitions/typing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::backend::ir::ir2_flat_contracts::{self as input_ir};
44
use crate::backend::types::{DataLocation, FunctionType, FunctionTypeKind, Type, TypeId};
55
use crate::cst::NodeId;
66

7-
impl Pass {
7+
impl Pass<'_> {
88
pub(super) fn type_of_identifier_path(
99
&mut self,
1010
identifier_path: &input_ir::IdentifierPath,

crates/solidity/outputs/cargo/crate/src/backend/passes/p4_type_definitions/visitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::backend::types::{DataLocation, Type};
1010
use crate::cst::TerminalKind;
1111
use crate::utils::versions::VERSION_0_5_0;
1212

13-
impl Visitor for Pass {
13+
impl Visitor for Pass<'_> {
1414
fn enter_source_unit(&mut self, node: &input_ir::SourceUnit) -> bool {
1515
self.enter_scope_for_node_id(node.node_id);
1616
true

crates/solidity/outputs/cargo/crate/src/backend/passes/p5_resolve_references/disambiguation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::backend::types::{FunctionType, Type, TypeId};
44
use crate::cst::NodeId;
55

66
/// Disambiguation functions that require typing (aka overload resolution)
7-
impl Pass {
7+
impl Pass<'_> {
88
fn get_function_definition_parameters(
99
&self,
1010
definition_id: Option<NodeId>,

0 commit comments

Comments
 (0)