Skip to content

Commit d50ce6a

Browse files
author
Vladimir Radosavljevic
committed
[v2] Error when contract linearisation is impossible
Add two diagnostics from the linearisation pass: 1) cyclic-inheritance for inheritance cycles. 2) linearisation-impossible for inconsistent C3 orderings. Add a binder test for the forward-reference case, where solc errors but slang intentionally accepts it. Signed-off-by: Vladimir Radosavljevic <vr@matterlabs.dev>
1 parent 8b39e06 commit d50ce6a

27 files changed

Lines changed: 487 additions & 22 deletions

File tree

crates/solidity-v2/outputs/cargo/common/generated/public_api.txt

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use serde::Serialize;
2+
3+
use crate::diagnostics::extensions::DiagnosticExtensions;
4+
use crate::diagnostics::severity::DiagnosticSeverity;
5+
6+
/// Diagnostic emitted when a contract's or interface's inheritance hierarchy
7+
/// contains a cycle.
8+
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
9+
pub struct CyclicInheritance;
10+
11+
impl DiagnosticExtensions for CyclicInheritance {
12+
fn severity(&self) -> DiagnosticSeverity {
13+
DiagnosticSeverity::Error
14+
}
15+
16+
fn code(&self) -> &'static str {
17+
"semantic/cyclic-inheritance"
18+
}
19+
20+
fn message(&self) -> String {
21+
"Circular inheritance is not allowed.".to_owned()
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use serde::Serialize;
2+
3+
use crate::diagnostics::extensions::DiagnosticExtensions;
4+
use crate::diagnostics::severity::DiagnosticSeverity;
5+
6+
/// Diagnostic emitted when the C3 linearisation of a contract's or interface's
7+
/// inheritance hierarchy cannot be computed.
8+
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
9+
pub struct LinearisationImpossible;
10+
11+
impl DiagnosticExtensions for LinearisationImpossible {
12+
fn severity(&self) -> DiagnosticSeverity {
13+
DiagnosticSeverity::Error
14+
}
15+
16+
fn code(&self) -> &'static str {
17+
"semantic/linearisation-impossible"
18+
}
19+
20+
fn message(&self) -> String {
21+
"Linearization of inheritance graph is impossible.".to_owned()
22+
}
23+
}

crates/solidity-v2/outputs/cargo/common/src/diagnostics/kinds/semantic/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
mod cyclic_constant_definition;
22
mod cyclic_constant_dependency;
33
mod cyclic_dependency_validator_exhausted;
4+
mod cyclic_inheritance;
5+
mod linearisation_impossible;
46

57
pub use cyclic_constant_definition::CyclicConstantDefinition;
68
pub use cyclic_constant_dependency::CyclicConstantDependency;
79
pub use cyclic_dependency_validator_exhausted::CyclicDependencyValidatorExhausted;
10+
pub use cyclic_inheritance::CyclicInheritance;
11+
pub use linearisation_impossible::LinearisationImpossible;
812
use serde::Serialize;
913

1014
use crate::diagnostics::kinds::utils::define_diagnostic_kind;
@@ -23,5 +27,10 @@ define_diagnostic_kind! {
2327
CyclicConstantDependency(CyclicConstantDependency),
2428
/// Constant dependency graph traversal exceeded the depth limit.
2529
CyclicDependencyValidatorExhausted(CyclicDependencyValidatorExhausted),
30+
/// A contract's or interface's inheritance hierarchy contains a cycle.
31+
CyclicInheritance(CyclicInheritance),
32+
/// The inheritance hierarchy cannot be linearised into a consistent
33+
/// method resolution order.
34+
LinearisationImpossible(LinearisationImpossible),
2635
}
2736
}

crates/solidity-v2/outputs/cargo/semantic/src/passes/p2_linearise_contracts/c3.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ use std::fmt::Debug;
33
use std::hash::Hash;
44

55
use slang_solidity_v2_common::collections::Map;
6+
/// The reason why linearisation failed.
7+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8+
pub(crate) enum LinearisationFailure {
9+
/// The inheritance graph contains a cycle.
10+
Cycle,
11+
/// The base orderings are inconsistent.
12+
Inconsistent,
13+
}
614

715
/// Produces a linearisation of a hierarchy of items using the C3 linearisation
816
/// algorithm. Given an item `A` with parents `(B1, B2)` in that order, the
@@ -18,7 +26,7 @@ use slang_solidity_v2_common::collections::Map;
1826
pub(crate) fn linearise<Item: Clone + Debug + Eq + Hash + PartialEq>(
1927
target: &Item,
2028
parents: &Map<Item, Vec<Item>>,
21-
) -> Option<Vec<Item>> {
29+
) -> Result<Vec<Item>, LinearisationFailure> {
2230
let mut linearisations: Map<Item, Vec<Item>> = Map::default();
2331

2432
// Keeps a running queue of pending linearisations.
@@ -52,7 +60,7 @@ pub(crate) fn linearise<Item: Clone + Debug + Eq + Hash + PartialEq>(
5260
}
5361
if merge_set.len() == item_parents.len() {
5462
merge_set.push(item_parents.clone());
55-
let merge_result = merge(merge_set)?;
63+
let merge_result = merge(merge_set).ok_or(LinearisationFailure::Inconsistent)?;
5664

5765
let mut result = Vec::new();
5866
result.push(item.clone());
@@ -73,7 +81,7 @@ pub(crate) fn linearise<Item: Clone + Debug + Eq + Hash + PartialEq>(
7381
if *check_item == item {
7482
if items_linearised == linearisations.len() {
7583
// no progress since last checkpoint; this indicates a cycle
76-
return None;
84+
return Err(LinearisationFailure::Cycle);
7785
}
7886
// Update progress and re-try
7987
checkpoint = Some((item.clone(), linearisations.len()));
@@ -88,7 +96,9 @@ pub(crate) fn linearise<Item: Clone + Debug + Eq + Hash + PartialEq>(
8896
}
8997
}
9098

91-
linearisations.remove(target)
99+
linearisations
100+
.remove(target)
101+
.ok_or(LinearisationFailure::Inconsistent)
92102
}
93103

94104
/// Merges the items in the set in C3 linearisation order. Returns None if
@@ -203,8 +213,8 @@ mod tests {
203213
parents.insert('D', vec!['B', 'C']);
204214
parents.insert('E', vec!['C', 'B']);
205215

206-
assert_eq!(Some(vec!['D', 'B', 'C', 'A']), linearise(&'D', &parents));
207-
assert_eq!(Some(vec!['E', 'C', 'B', 'A']), linearise(&'E', &parents));
216+
assert_eq!(Ok(vec!['D', 'B', 'C', 'A']), linearise(&'D', &parents));
217+
assert_eq!(Ok(vec!['E', 'C', 'B', 'A']), linearise(&'E', &parents));
208218
}
209219

210220
#[test]
@@ -214,7 +224,10 @@ mod tests {
214224
parents.insert('A', vec!['X']);
215225
parents.insert('C', vec!['X', 'A']);
216226

217-
assert_eq!(None, linearise(&'C', &parents));
227+
assert_eq!(
228+
Err(LinearisationFailure::Inconsistent),
229+
linearise(&'C', &parents)
230+
);
218231
}
219232

220233
#[test]
@@ -223,8 +236,8 @@ mod tests {
223236
parents.insert('B', vec!['A']);
224237
parents.insert('A', vec!['B']);
225238

226-
assert_eq!(None, linearise(&'A', &parents));
227-
assert_eq!(None, linearise(&'B', &parents));
239+
assert_eq!(Err(LinearisationFailure::Cycle), linearise(&'A', &parents));
240+
assert_eq!(Err(LinearisationFailure::Cycle), linearise(&'B', &parents));
228241
}
229242

230243
#[test]
@@ -234,8 +247,8 @@ mod tests {
234247
parents.insert('Y', vec!['Z']);
235248
parents.insert('Z', vec!['X']);
236249

237-
assert_eq!(None, linearise(&'X', &parents));
238-
assert_eq!(None, linearise(&'Y', &parents));
239-
assert_eq!(None, linearise(&'Z', &parents));
250+
assert_eq!(Err(LinearisationFailure::Cycle), linearise(&'X', &parents));
251+
assert_eq!(Err(LinearisationFailure::Cycle), linearise(&'Y', &parents));
252+
assert_eq!(Err(LinearisationFailure::Cycle), linearise(&'Z', &parents));
240253
}
241254
}

0 commit comments

Comments
 (0)