Skip to content

Commit 024659c

Browse files
committed
Add support for struct functions; refactor CallType and self code
1 parent b079956 commit 024659c

191 files changed

Lines changed: 8398 additions & 5999 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/analyzer/src/builtins.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use strum::{AsRefStr, EnumIter, EnumString};
22

3-
#[derive(Debug, PartialEq, EnumString)]
3+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, EnumString, AsRefStr)]
44
#[strum(serialize_all = "snake_case")]
55
pub enum ValueMethod {
66
Clone,
@@ -10,29 +10,36 @@ pub enum ValueMethod {
1010

1111
#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumString, AsRefStr, Hash, EnumIter)]
1212
#[strum(serialize_all = "snake_case")]
13-
pub enum GlobalMethod {
13+
pub enum GlobalFunction {
1414
Keccak256,
1515
SendValue,
1616
Balance,
1717
BalanceOf,
1818
}
1919

20-
#[derive(Debug, PartialEq, EnumString, AsRefStr)]
20+
#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString, AsRefStr)]
2121
#[strum(serialize_all = "snake_case")]
2222
pub enum ContractTypeMethod {
2323
Create,
2424
Create2,
2525
}
2626

27+
impl ContractTypeMethod {
28+
pub fn arg_count(&self) -> usize {
29+
match self {
30+
ContractTypeMethod::Create => 1,
31+
ContractTypeMethod::Create2 => 2,
32+
}
33+
}
34+
}
35+
2736
#[derive(Copy, Clone, Debug, Eq, PartialEq, EnumString, EnumIter, AsRefStr)]
2837
#[strum(serialize_all = "lowercase")]
29-
pub enum Object {
38+
pub enum GlobalObject {
3039
Block,
3140
Chain,
3241
Msg,
3342
Tx,
34-
#[strum(serialize = "self")]
35-
Self_,
3643
}
3744

3845
#[derive(Debug, PartialEq, EnumString)]
@@ -67,6 +74,6 @@ pub enum TxField {
6774

6875
#[derive(Debug, PartialEq, EnumString)]
6976
#[strum(serialize_all = "snake_case")]
70-
pub enum SelfField {
77+
pub enum ContractSelfField {
7178
Address,
7279
}

crates/analyzer/src/context.rs

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use crate::builtins::GlobalMethod;
1+
use crate::builtins::{ContractTypeMethod, GlobalFunction, ValueMethod};
22
use crate::errors::{self, CannotMove, TypeError};
3-
use crate::namespace::items::{DiagnosticSink, EventId, FunctionId, Item};
4-
use crate::namespace::types::{FixedSize, Type};
3+
use crate::namespace::items::{Class, ContractId, DiagnosticSink, EventId, FunctionId, Item};
4+
use crate::namespace::types::{FixedSize, SelfDecl, Type};
55
use crate::AnalyzerDb;
66
use fe_common::diagnostics::Diagnostic;
77
pub use fe_common::diagnostics::Label;
@@ -96,6 +96,16 @@ pub trait AnalyzerContext {
9696
#[derive(Clone, Debug, PartialEq, Eq)]
9797
pub enum NamedThing {
9898
Item(Item),
99+
SelfValue {
100+
/// Function `self` parameter.
101+
decl: Option<SelfDecl>,
102+
103+
/// The function's parent, if any. If `None`, `self` has been
104+
/// used in a module-level function.
105+
class: Option<Class>,
106+
span: Option<Span>,
107+
},
108+
// SelfType // when/if we add a `Self` type keyword
99109
Variable {
100110
name: String,
101111
typ: Result<FixedSize, TypeError>,
@@ -107,6 +117,7 @@ impl NamedThing {
107117
pub fn name_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
108118
match self {
109119
NamedThing::Item(item) => item.name_span(db),
120+
NamedThing::SelfValue { span, .. } => *span,
110121
NamedThing::Variable { span, .. } => Some(*span),
111122
}
112123
}
@@ -115,13 +126,15 @@ impl NamedThing {
115126
match self {
116127
NamedThing::Item(item) => item.is_builtin(),
117128
NamedThing::Variable { .. } => false,
129+
NamedThing::SelfValue { .. } => false,
118130
}
119131
}
120132

121133
pub fn item_kind_display_name(&self) -> &str {
122134
match self {
123135
NamedThing::Item(item) => item.item_kind_display_name(),
124136
NamedThing::Variable { .. } => "variable",
137+
NamedThing::SelfValue { .. } => "value",
125138
}
126139
}
127140
}
@@ -210,16 +223,15 @@ impl ExpressionAttributes {
210223
/// Adds a move to value, if it is in storage or memory.
211224
pub fn into_loaded(mut self) -> Result<Self, CannotMove> {
212225
match self.typ {
213-
Type::Base(_) => {}
214-
Type::Contract(_) => {}
215-
_ => return Err(CannotMove),
216-
}
226+
Type::Base(_) | Type::Contract(_) => {
227+
if self.location != Location::Value {
228+
self.move_location = Some(Location::Value);
229+
}
217230

218-
if self.location != Location::Value {
219-
self.move_location = Some(Location::Value);
231+
Ok(self)
232+
}
233+
_ => Err(CannotMove),
220234
}
221-
222-
Ok(self)
223235
}
224236

225237
/// The final location of an expression after a possible move.
@@ -233,21 +245,77 @@ impl ExpressionAttributes {
233245

234246
impl fmt::Display for ExpressionAttributes {
235247
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
236-
write!(
237-
f,
238-
"{}: {:?} => {:?}",
239-
self.typ, self.location, self.move_location
240-
)
248+
if let Some(move_to) = self.move_location {
249+
write!(f, "{}: {:?} => {:?}", self.typ, self.location, move_to)
250+
} else {
251+
write!(f, "{}: {:?}", self.typ, self.location)
252+
}
241253
}
242254
}
243255

244256
/// The type of a function call.
245257
#[derive(Clone, Debug, PartialEq, Eq)]
246258
pub enum CallType {
247-
BuiltinFunction(GlobalMethod),
248-
TypeConstructor { typ: Type },
249-
SelfAttribute { func_name: String, self_span: Span },
259+
BuiltinFunction(GlobalFunction),
260+
BuiltinValueMethod(ValueMethod),
261+
262+
// create, create2 (will be methods of the context struct soon)
263+
BuiltinAssociatedFunction {
264+
contract: ContractId,
265+
function: ContractTypeMethod,
266+
},
267+
268+
// MyStruct.foo() (soon MyStruct::foo())
269+
AssociatedFunction {
270+
class: Class,
271+
function: FunctionId,
272+
},
273+
ValueMethod {
274+
is_self: bool,
275+
class: Class,
276+
method: FunctionId,
277+
},
250278
Pure(FunctionId),
251-
ValueAttribute,
252-
TypeAttribute { typ: Type, func_name: String },
279+
TypeConstructor(Type),
280+
}
281+
282+
impl CallType {
283+
pub fn function(&self) -> Option<FunctionId> {
284+
use CallType::*;
285+
match self {
286+
BuiltinFunction(_)
287+
| BuiltinValueMethod(_)
288+
| TypeConstructor(_)
289+
| BuiltinAssociatedFunction { .. } => None,
290+
AssociatedFunction { function: id, .. } | ValueMethod { method: id, .. } | Pure(id) => {
291+
Some(*id)
292+
}
293+
}
294+
}
295+
296+
pub fn function_name(&self, db: &dyn AnalyzerDb) -> String {
297+
match self {
298+
CallType::BuiltinFunction(f) => f.as_ref().to_string(),
299+
CallType::BuiltinValueMethod(f) => f.as_ref().to_string(),
300+
CallType::BuiltinAssociatedFunction { function, .. } => function.as_ref().to_string(),
301+
302+
CallType::AssociatedFunction { function: id, .. }
303+
| CallType::ValueMethod { method: id, .. }
304+
| CallType::Pure(id) => id.name(db),
305+
CallType::TypeConstructor(typ) => typ.to_string(),
306+
}
307+
}
308+
309+
pub fn is_unsafe(&self, db: &dyn AnalyzerDb) -> bool {
310+
// There are no built-in unsafe fns yet
311+
self.function()
312+
.map(|id| id.unsafe_span(db).is_some())
313+
.unwrap_or(false)
314+
}
315+
}
316+
317+
impl fmt::Display for CallType {
318+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
319+
write!(f, "{:?}", self)
320+
}
253321
}

crates/analyzer/src/db.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ pub trait AnalyzerDb {
7373
fn contract_function_map(&self, id: ContractId) -> Analysis<Rc<IndexMap<String, FunctionId>>>;
7474
#[salsa::invoke(queries::contracts::contract_public_function_map)]
7575
fn contract_public_function_map(&self, id: ContractId) -> Rc<IndexMap<String, FunctionId>>;
76-
#[salsa::invoke(queries::contracts::contract_pure_function_map)]
77-
fn contract_pure_function_map(&self, id: ContractId) -> Rc<IndexMap<String, FunctionId>>;
78-
#[salsa::invoke(queries::contracts::contract_self_function_map)]
79-
fn contract_self_function_map(&self, id: ContractId) -> Rc<IndexMap<String, FunctionId>>;
8076
#[salsa::invoke(queries::contracts::contract_init_function)]
8177
fn contract_init_function(&self, id: ContractId) -> Analysis<Option<FunctionId>>;
8278

@@ -114,6 +110,10 @@ pub trait AnalyzerDb {
114110
&self,
115111
field: StructFieldId,
116112
) -> Analysis<Result<types::FixedSize, TypeError>>;
113+
#[salsa::invoke(queries::structs::struct_all_functions)]
114+
fn struct_all_functions(&self, id: StructId) -> Rc<Vec<FunctionId>>;
115+
#[salsa::invoke(queries::structs::struct_function_map)]
116+
fn struct_function_map(&self, id: StructId) -> Analysis<Rc<IndexMap<String, FunctionId>>>;
117117

118118
// Event
119119
#[salsa::invoke(queries::events::event_type)]

crates/analyzer/src/db/queries/contracts.rs

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use indexmap::map::{Entry, IndexMap};
1111

1212
use std::rc::Rc;
1313

14+
/// A `Vec` of every function defined in the contract, including duplicates and the init function.
1415
pub fn contract_all_functions(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<Vec<FunctionId>> {
1516
let module = contract.module(db);
1617
let body = &contract.data(db).ast.kind.body;
@@ -21,8 +22,8 @@ pub fn contract_all_functions(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<V
2122
ast::ContractStmt::Function(node) => {
2223
Some(db.intern_function(Rc::new(items::Function {
2324
ast: node.clone(),
24-
contract: Some(contract),
2525
module,
26+
parent: Some(items::Class::Contract(contract)),
2627
})))
2728
}
2829
})
@@ -37,7 +38,7 @@ pub fn contract_function_map(
3738
let mut scope = ItemScope::new(db, contract.module(db));
3839
let mut map = IndexMap::<String, FunctionId>::new();
3940

40-
for func in contract.all_functions(db).iter() {
41+
for func in db.contract_all_functions(contract).iter() {
4142
let def = &func.data(db).ast;
4243
let def_name = def.name();
4344
if def_name == "__init__" {
@@ -102,37 +103,11 @@ pub fn contract_public_function_map(
102103
)
103104
}
104105

105-
pub fn contract_pure_function_map(
106-
db: &dyn AnalyzerDb,
107-
contract: ContractId,
108-
) -> Rc<IndexMap<String, FunctionId>> {
109-
Rc::new(
110-
contract
111-
.functions(db)
112-
.iter()
113-
.filter_map(|(name, func)| func.is_pure(db).then(|| (name.clone(), *func)))
114-
.collect(),
115-
)
116-
}
117-
118-
pub fn contract_self_function_map(
119-
db: &dyn AnalyzerDb,
120-
contract: ContractId,
121-
) -> Rc<IndexMap<String, FunctionId>> {
122-
Rc::new(
123-
contract
124-
.functions(db)
125-
.iter()
126-
.filter_map(|(name, func)| (!func.is_pure(db)).then(|| (name.clone(), *func)))
127-
.collect(),
128-
)
129-
}
130-
131106
pub fn contract_init_function(
132107
db: &dyn AnalyzerDb,
133108
contract: ContractId,
134109
) -> Analysis<Option<FunctionId>> {
135-
let all_fns = contract.all_functions(db);
110+
let all_fns = db.contract_all_functions(contract);
136111
let mut init_fns = all_fns.iter().filter_map(|func| {
137112
let def = &func.data(db).ast;
138113
(def.name() == "__init__").then(|| (func, def.span))
@@ -180,6 +155,7 @@ pub fn contract_init_function(
180155
}
181156
}
182157

158+
/// A `Vec` of all events defined within the contract, including those with duplicate names.
183159
pub fn contract_all_events(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<Vec<EventId>> {
184160
let body = &contract.data(db).ast.kind.body;
185161
Rc::new(
@@ -227,6 +203,7 @@ pub fn contract_event_map(
227203
}
228204
}
229205

206+
/// All field ids, including those with duplicate names
230207
pub fn contract_all_fields(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<Vec<ContractFieldId>> {
231208
let fields = contract
232209
.data(db)

crates/analyzer/src/db/queries/functions.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::context::{AnalyzerContext, FunctionBody};
22
use crate::db::{Analysis, AnalyzerDb};
33
use crate::errors::TypeError;
4-
use crate::namespace::items::FunctionId;
4+
use crate::namespace::items::{Class, FunctionId};
55
use crate::namespace::scopes::{BlockScope, BlockScopeType, FunctionScope, ItemScope};
66
use crate::namespace::types::{self, FixedSize, SelfDecl};
77
use crate::traversal::functions::traverse_statements;
@@ -24,10 +24,10 @@ pub fn function_signature(
2424
let def = &node.kind;
2525

2626
let mut scope = ItemScope::new(db, function.module(db));
27-
let contract = function.contract(db);
27+
let fn_parent = function.parent(db);
2828

2929
if_chain! {
30-
if contract.is_some();
30+
if let Some(Class::Contract(_)) = fn_parent;
3131
if let Some(pub_span) = function.pub_span(db);
3232
if let Some(unsafe_span) = function.unsafe_span(db);
3333
then {
@@ -37,22 +37,22 @@ pub fn function_signature(
3737
}
3838
}
3939

40-
let mut self_decl = SelfDecl::None;
40+
let mut self_decl = None;
4141
let mut names = HashMap::new();
4242
let params = def
4343
.args
4444
.iter()
4545
.enumerate()
4646
.filter_map(|(index, arg)| match &arg.kind {
4747
ast::FunctionArg::Zelf => {
48-
if contract.is_none() {
48+
if fn_parent.is_none() {
4949
scope.error(
50-
"`self` can only be used in contract functions",
50+
"`self` can only be used in contract or struct functions",
5151
arg.span,
52-
"not allowed in functions defined outside of a contract",
52+
"not allowed in functions defined outside of a contract or struct",
5353
);
5454
} else {
55-
self_decl = SelfDecl::Mutable;
55+
self_decl = Some(SelfDecl::Mutable);
5656
if index != 0 {
5757
scope.error(
5858
"`self` is not the first parameter",

0 commit comments

Comments
 (0)