Skip to content

Commit 2f8a917

Browse files
committed
Support implementing trait functions in impl blocks
1 parent dee378a commit 2f8a917

34 files changed

+1587
-901
lines changed

crates/analyzer/src/context.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use crate::namespace::items::{Class, ContractId, DiagnosticSink, EventId, FunctionId, Item};
2-
use crate::namespace::types::{SelfDecl, Struct, Type};
1+
use crate::namespace::items::{
2+
Class, ContractId, DiagnosticSink, EventId, FunctionId, FunctionSigId, Item, TraitId,
3+
};
4+
use crate::namespace::types::{Generic, SelfDecl, Struct, Type};
35
use crate::AnalyzerDb;
46
use crate::{
57
builtins::{ContractTypeMethod, GlobalFunction, Intrinsic, ValueMethod},
@@ -361,7 +363,12 @@ impl Location {
361363
pub fn assign_location(typ: &Type) -> Self {
362364
match typ {
363365
Type::Base(_) | Type::Contract(_) => Location::Value,
364-
Type::Array(_) | Type::Tuple(_) | Type::String(_) | Type::Struct(_) => Location::Memory,
366+
// For now assume that generics can only ever refer to structs
367+
Type::Array(_)
368+
| Type::Tuple(_)
369+
| Type::String(_)
370+
| Type::Struct(_)
371+
| Type::Generic(_) => Location::Memory,
365372
other => panic!("Type {other} can not be assigned, returned or passed"),
366373
}
367374
}
@@ -458,11 +465,24 @@ pub enum CallType {
458465
class: Class,
459466
function: FunctionId,
460467
},
468+
// some_struct_or_contract.foo()
461469
ValueMethod {
462470
is_self: bool,
463471
class: Class,
464472
method: FunctionId,
465473
},
474+
// some_trait.foo()
475+
// The reason this can not use `ValueMethod` is because the trait might not have a function implementation
476+
// and even if it had it might not be the one that ends up getting executed. An `impl` block will decide that.
477+
TraitValueMethod {
478+
is_self: bool,
479+
// Might want to drop this since it's a bit redundant with method.parent()
480+
trait_id: TraitId,
481+
method: FunctionSigId,
482+
// Traits can not directly be used as types but can act as bounds for generics. This is the generic type
483+
// that the method is called on.
484+
generic_type: Generic,
485+
},
466486
External {
467487
contract: ContractId,
468488
function: FunctionId,
@@ -479,6 +499,7 @@ impl CallType {
479499
| BuiltinValueMethod { .. }
480500
| TypeConstructor(_)
481501
| Intrinsic(_)
502+
| TraitValueMethod { .. }
482503
| BuiltinAssociatedFunction { .. } => None,
483504
AssociatedFunction { function: id, .. }
484505
| ValueMethod { method: id, .. }
@@ -497,6 +518,7 @@ impl CallType {
497518
| CallType::ValueMethod { method: id, .. }
498519
| CallType::External { function: id, .. }
499520
| CallType::Pure(id) => id.name(db),
521+
CallType::TraitValueMethod { method: id, .. } => id.name(db),
500522
CallType::TypeConstructor(typ) => typ.name(),
501523
}
502524
}

crates/analyzer/src/db.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::context::{Analysis, Constant, FunctionBody};
22
use crate::errors::{ConstEvalError, TypeError};
33
use crate::namespace::items::{
4-
self, ContractFieldId, ContractId, DepGraphWrapper, EventId, FunctionId, Impl, IngotId, Item,
5-
ModuleConstantId, ModuleId, StructFieldId, StructId, TraitId, TypeAliasId,
4+
self, ContractFieldId, ContractId, DepGraphWrapper, EventId, FunctionId, FunctionSigId, ImplId,
5+
IngotId, Item, ModuleConstantId, ModuleId, StructFieldId, StructId, TraitId, TypeAliasId,
66
};
77
use crate::namespace::types;
88
use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut};
@@ -26,6 +26,8 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
2626
#[salsa::interned]
2727
fn intern_trait(&self, data: Rc<items::Trait>) -> TraitId;
2828
#[salsa::interned]
29+
fn intern_impl(&self, data: Rc<items::Impl>) -> ImplId;
30+
#[salsa::interned]
2931
fn intern_struct_field(&self, data: Rc<items::StructField>) -> StructFieldId;
3032
#[salsa::interned]
3133
fn intern_type_alias(&self, data: Rc<items::TypeAlias>) -> TypeAliasId;
@@ -34,6 +36,8 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
3436
#[salsa::interned]
3537
fn intern_contract_field(&self, data: Rc<items::ContractField>) -> ContractFieldId;
3638
#[salsa::interned]
39+
fn intern_function_sig(&self, data: Rc<items::FunctionSig>) -> FunctionSigId;
40+
#[salsa::interned]
3741
fn intern_function(&self, data: Rc<items::Function>) -> FunctionId;
3842
#[salsa::interned]
3943
fn intern_event(&self, data: Rc<items::Event>) -> EventId;
@@ -63,7 +67,7 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
6367
#[salsa::invoke(queries::module::module_all_items)]
6468
fn module_all_items(&self, module: ModuleId) -> Rc<[Item]>;
6569
#[salsa::invoke(queries::module::module_all_impls)]
66-
fn module_all_impls(&self, module: ModuleId) -> Rc<[Impl]>;
70+
fn module_all_impls(&self, module: ModuleId) -> Rc<[ImplId]>;
6771
#[salsa::invoke(queries::module::module_item_map)]
6872
fn module_item_map(&self, module: ModuleId) -> Analysis<Rc<IndexMap<SmolStr, Item>>>;
6973
#[salsa::invoke(queries::module::module_contracts)]
@@ -134,7 +138,7 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
134138

135139
// Function
136140
#[salsa::invoke(queries::functions::function_signature)]
137-
fn function_signature(&self, id: FunctionId) -> Analysis<Rc<types::FunctionSignature>>;
141+
fn function_signature(&self, id: FunctionSigId) -> Analysis<Rc<types::FunctionSignature>>;
138142
#[salsa::invoke(queries::functions::function_body)]
139143
fn function_body(&self, id: FunctionId) -> Analysis<Rc<FunctionBody>>;
140144
#[salsa::cycle(queries::functions::function_dependency_graph_cycle)]
@@ -161,6 +165,12 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
161165
// Trait
162166
#[salsa::invoke(queries::traits::trait_type)]
163167
fn trait_type(&self, id: TraitId) -> Rc<types::Trait>;
168+
#[salsa::invoke(queries::traits::trait_all_functions)]
169+
fn trait_all_functions(&self, id: TraitId) -> Rc<[FunctionSigId]>;
170+
171+
// Impl
172+
#[salsa::invoke(queries::traits::impl_all_functions)]
173+
fn impl_all_functions(&self, id: ImplId) -> Rc<[FunctionId]>;
164174

165175
// Event
166176
#[salsa::invoke(queries::events::event_type)]

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,9 @@ pub fn contract_all_functions(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<[
2222
body.iter()
2323
.filter_map(|stmt| match stmt {
2424
ast::ContractStmt::Event(_) => None,
25-
ast::ContractStmt::Function(node) => {
26-
Some(db.intern_function(Rc::new(items::Function {
27-
ast: node.clone(),
28-
module,
29-
parent: Some(items::Class::Contract(contract)),
30-
})))
31-
}
25+
ast::ContractStmt::Function(node) => Some(db.intern_function(Rc::new(
26+
items::Function::new(db, node, Some(items::Class::Contract(contract)), module),
27+
))),
3228
})
3329
.collect()
3430
}
@@ -53,7 +49,7 @@ pub fn contract_function_map(
5349
def_name,
5450
&NamedThing::Item(Item::Event(event)),
5551
Some(event.name_span(db)),
56-
def.kind.name.span,
52+
def.kind.sig.kind.name.span,
5753
);
5854
continue;
5955
}
@@ -64,7 +60,7 @@ pub fn contract_function_map(
6460
def_name,
6561
&named_item,
6662
named_item.name_span(db),
67-
def.kind.name.span,
63+
def.kind.sig.kind.name.span,
6864
);
6965
continue;
7066
}

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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::context::{AnalyzerContext, CallType, FunctionBody};
22
use crate::db::{Analysis, AnalyzerDb};
33
use crate::errors::TypeError;
44
use crate::namespace::items::{
5-
Class, DepGraph, DepGraphWrapper, DepLocality, FunctionId, Item, TypeDef,
5+
Class, DepGraph, DepGraphWrapper, DepLocality, FunctionId, FunctionSigId, Item, TypeDef,
66
};
77
use crate::namespace::scopes::{BlockScope, BlockScopeType, FunctionScope, ItemScope};
88
use crate::namespace::types::{self, Contract, CtxDecl, Generic, SelfDecl, Struct, Type};
@@ -19,10 +19,9 @@ use std::rc::Rc;
1919
/// errors. Does not inspect the function body.
2020
pub fn function_signature(
2121
db: &dyn AnalyzerDb,
22-
function: FunctionId,
22+
function: FunctionSigId,
2323
) -> Analysis<Rc<types::FunctionSignature>> {
24-
let node = &function.data(db).ast;
25-
let def = &node.kind;
24+
let def = &function.data(db).ast;
2625

2726
let mut scope = ItemScope::new(db, function.module(db));
2827
let fn_parent = function.class(db);
@@ -44,6 +43,7 @@ pub fn function_signature(
4443
}
4544

4645
let params = def
46+
.kind
4747
.args
4848
.iter()
4949
.enumerate()
@@ -114,9 +114,9 @@ pub fn function_signature(
114114
if label.kind != "_";
115115
if let Some(dup_idx) = labels.get(&label.kind);
116116
then {
117-
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
117+
let dup_arg: &Node<ast::FunctionArg> = &def.kind.args[*dup_idx];
118118
scope.fancy_error(
119-
&format!("duplicate parameter labels in function `{}`", def.name.kind),
119+
&format!("duplicate parameter labels in function `{}`", def.kind.name.kind),
120120
vec![
121121
Label::primary(dup_arg.span, "the label `{}` was first used here"),
122122
Label::primary(label.span, "label `{}` used again here"),
@@ -138,9 +138,9 @@ pub fn function_signature(
138138
);
139139
None
140140
} else if let Some(dup_idx) = names.get(&reg.name.kind) {
141-
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
141+
let dup_arg: &Node<ast::FunctionArg> = &def.kind.args[*dup_idx];
142142
scope.duplicate_name_error(
143-
&format!("duplicate parameter names in function `{}`", def.name.kind),
143+
&format!("duplicate parameter names in function `{}`", function.name(db)),
144144
&reg.name.kind,
145145
dup_arg.span,
146146
arg.span,
@@ -159,10 +159,11 @@ pub fn function_signature(
159159
.collect();
160160

161161
let return_type = def
162+
.kind
162163
.return_type
163164
.as_ref()
164165
.map(|type_node| {
165-
let fn_name = &def.name.kind;
166+
let fn_name = &function.name(db);
166167
if fn_name == "__init__" || fn_name == "__call__" {
167168
// `__init__` and `__call__` must not return any type other than `()`.
168169
if type_node.kind != ast::TypeDesc::Unit {
@@ -207,7 +208,7 @@ pub fn function_signature(
207208

208209
pub fn resolve_function_param_type(
209210
db: &dyn AnalyzerDb,
210-
function: FunctionId,
211+
function: FunctionSigId,
211212
context: &mut dyn AnalyzerContext,
212213
desc: &Node<ast::TypeDesc>,
213214
) -> Result<Type, TypeError> {
@@ -254,11 +255,11 @@ pub fn function_body(db: &dyn AnalyzerDb, function: FunctionId) -> Analysis<Rc<F
254255
"function body is missing a return or revert statement",
255256
vec![
256257
Label::primary(
257-
def.name.span,
258+
function.name_span(db),
258259
"all paths of this function must `return` or `revert`",
259260
),
260261
Label::secondary(
261-
def.return_type.as_ref().unwrap().span,
262+
def.sig.kind.return_type.as_ref().unwrap().span,
262263
format!("expected function to return `{}`", return_type),
263264
),
264265
],
@@ -362,6 +363,13 @@ pub fn function_dependency_graph(db: &dyn AnalyzerDb, function: FunctionId) -> D
362363
directs.push((root, class.as_item(), DepLocality::Local));
363364
directs.push((root, Item::Function(*method), DepLocality::Local));
364365
}
366+
CallType::TraitValueMethod { trait_id, .. } => {
367+
directs.push((
368+
root,
369+
Item::Type(TypeDef::Trait(*trait_id)),
370+
DepLocality::Local,
371+
));
372+
}
365373
CallType::External { contract, function } => {
366374
directs.push((root, Item::Function(*function), DepLocality::External));
367375
// Probably redundant:

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::context::{Analysis, AnalyzerContext, Constant};
22
use crate::db::AnalyzerDb;
33
use crate::errors::{self, ConstEvalError, TypeError};
44
use crate::namespace::items::{
5-
Contract, ContractId, Event, Function, Impl, Item, ModuleConstant, ModuleConstantId, ModuleId,
6-
ModuleSource, Struct, StructId, Trait, TypeAlias, TypeDef,
5+
Contract, ContractId, Event, Function, Impl, ImplId, Item, ModuleConstant, ModuleConstantId,
6+
ModuleId, ModuleSource, Struct, StructId, Trait, TypeAlias, TypeDef,
77
};
88
use crate::namespace::scopes::ItemScope;
99
use crate::namespace::types::{self, Type};
@@ -86,13 +86,9 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
8686
module,
8787
}),
8888
))),
89-
ast::ModuleStmt::Function(node) => {
90-
Some(Item::Function(db.intern_function(Rc::new(Function {
91-
ast: node.clone(),
92-
module,
93-
parent: None,
94-
}))))
95-
}
89+
ast::ModuleStmt::Function(node) => Some(Item::Function(
90+
db.intern_function(Rc::new(Function::new(db, node, None, module))),
91+
)),
9692
ast::ModuleStmt::Trait(node) => Some(Item::Type(TypeDef::Trait(db.intern_trait(
9793
Rc::new(Trait {
9894
ast: node.clone(),
@@ -110,7 +106,7 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
110106
.collect()
111107
}
112108

113-
pub fn module_all_impls(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Impl]> {
109+
pub fn module_all_impls(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[ImplId]> {
114110
let body = &module.ast(db).body;
115111
body.iter()
116112
.filter_map(|stmt| match stmt {
@@ -124,12 +120,12 @@ pub fn module_all_impls(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Impl]> {
124120
let receiver_type = type_desc(&mut scope, &impl_node.kind.receiver).unwrap();
125121

126122
if let Some(Item::Type(TypeDef::Trait(val))) = treit {
127-
Some(Impl {
123+
Some(db.intern_impl(Rc::new(Impl {
128124
trait_id: val,
129125
receiver: receiver_type,
130126
ast: impl_node.clone(),
131127
module,
132-
})
128+
})))
133129
} else {
134130
None
135131
}

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use crate::context::AnalyzerContext;
33
use crate::db::Analysis;
44
use crate::errors::TypeError;
55
use crate::namespace::items::{
6-
self, DepGraph, DepGraphWrapper, DepLocality, Function, FunctionId, Item, StructField,
7-
StructFieldId, StructId, TypeDef,
6+
self, DepGraph, DepGraphWrapper, DepLocality, FunctionId, Item, StructField, StructFieldId,
7+
StructId, TypeDef,
88
};
99
use crate::namespace::scopes::ItemScope;
1010
use crate::namespace::types::{self, Contract, Struct, Type};
@@ -121,11 +121,12 @@ pub fn struct_all_functions(db: &dyn AnalyzerDb, struct_: StructId) -> Rc<[Funct
121121
.functions
122122
.iter()
123123
.map(|node| {
124-
db.intern_function(Rc::new(Function {
125-
ast: node.clone(),
126-
module: struct_data.module,
127-
parent: Some(items::Class::Struct(struct_)),
128-
}))
124+
db.intern_function(Rc::new(items::Function::new(
125+
db,
126+
node,
127+
Some(items::Class::Struct(struct_)),
128+
struct_data.module,
129+
)))
129130
})
130131
.collect()
131132
}
@@ -150,7 +151,7 @@ pub fn struct_function_map(
150151
def_name,
151152
&named_item,
152153
named_item.name_span(db),
153-
def.kind.name.span,
154+
func.name_span(db),
154155
);
155156
continue;
156157
}
@@ -161,7 +162,7 @@ pub fn struct_function_map(
161162
"function name `{}` conflicts with built-in function",
162163
def_name
163164
),
164-
def.kind.name.span,
165+
func.name_span(db),
165166
&format!("`{}` is a built-in function", def_name),
166167
);
167168
continue;

0 commit comments

Comments
 (0)