Skip to content

Commit 9ff971e

Browse files
committed
Support implementing trait functions in impl blocks
1 parent dee378a commit 9ff971e

File tree

60 files changed

+2040
-1016
lines changed

Some content is hidden

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

60 files changed

+2040
-1016
lines changed

crates/analyzer/src/context.rs

+23-4
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,21 @@ pub enum CallType {
458465
class: Class,
459466
function: FunctionId,
460467
},
468+
// some_struct_or_contract.foo()
461469
ValueMethod {
462-
is_self: bool,
463470
class: Class,
464471
method: FunctionId,
465472
},
473+
// some_trait.foo()
474+
// The reason this can not use `ValueMethod` is mainly because the trait might not have a function implementation
475+
// and even if it had it might not be the one that ends up getting executed. An `impl` block will decide that.
476+
TraitValueMethod {
477+
trait_id: TraitId,
478+
method: FunctionSigId,
479+
// Traits can not directly be used as types but can act as bounds for generics. This is the generic type
480+
// that the method is called on.
481+
generic_type: Generic,
482+
},
466483
External {
467484
contract: ContractId,
468485
function: FunctionId,
@@ -479,6 +496,7 @@ impl CallType {
479496
| BuiltinValueMethod { .. }
480497
| TypeConstructor(_)
481498
| Intrinsic(_)
499+
| TraitValueMethod { .. }
482500
| BuiltinAssociatedFunction { .. } => None,
483501
AssociatedFunction { function: id, .. }
484502
| ValueMethod { method: id, .. }
@@ -497,6 +515,7 @@ impl CallType {
497515
| CallType::ValueMethod { method: id, .. }
498516
| CallType::External { function: id, .. }
499517
| CallType::Pure(id) => id.name(db),
518+
CallType::TraitValueMethod { method: id, .. } => id.name(db),
500519
CallType::TypeConstructor(typ) => typ.name(),
501520
}
502521
}

crates/analyzer/src/db.rs

+21-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
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
};
7-
use crate::namespace::types;
7+
use crate::namespace::types::{self, Type};
88
use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut};
99
use fe_common::{SourceFileId, Span};
1010
use fe_parser::ast;
@@ -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,9 +67,11 @@ 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>>>;
73+
#[salsa::invoke(queries::module::module_impl_map)]
74+
fn module_impl_map(&self, module: ModuleId) -> Analysis<Rc<IndexMap<(TraitId, Type), ImplId>>>;
6975
#[salsa::invoke(queries::module::module_contracts)]
7076
fn module_contracts(&self, module: ModuleId) -> Rc<[ContractId]>;
7177
#[salsa::invoke(queries::module::module_structs)]
@@ -134,7 +140,7 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
134140

135141
// Function
136142
#[salsa::invoke(queries::functions::function_signature)]
137-
fn function_signature(&self, id: FunctionId) -> Analysis<Rc<types::FunctionSignature>>;
143+
fn function_signature(&self, id: FunctionSigId) -> Analysis<Rc<types::FunctionSignature>>;
138144
#[salsa::invoke(queries::functions::function_body)]
139145
fn function_body(&self, id: FunctionId) -> Analysis<Rc<FunctionBody>>;
140146
#[salsa::cycle(queries::functions::function_dependency_graph_cycle)]
@@ -161,6 +167,16 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
161167
// Trait
162168
#[salsa::invoke(queries::traits::trait_type)]
163169
fn trait_type(&self, id: TraitId) -> Rc<types::Trait>;
170+
#[salsa::invoke(queries::traits::trait_all_functions)]
171+
fn trait_all_functions(&self, id: TraitId) -> Rc<[FunctionSigId]>;
172+
#[salsa::invoke(queries::traits::trait_function_map)]
173+
fn trait_function_map(&self, id: TraitId) -> Analysis<Rc<IndexMap<SmolStr, FunctionSigId>>>;
174+
175+
// Impl
176+
#[salsa::invoke(queries::impls::impl_all_functions)]
177+
fn impl_all_functions(&self, id: ImplId) -> Rc<[FunctionId]>;
178+
#[salsa::invoke(queries::impls::impl_function_map)]
179+
fn impl_function_map(&self, id: ImplId) -> Analysis<Rc<IndexMap<SmolStr, FunctionId>>>;
164180

165181
// Event
166182
#[salsa::invoke(queries::events::event_type)]

crates/analyzer/src/db/queries.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod contracts;
22
pub mod events;
33
pub mod functions;
4+
pub mod impls;
45
pub mod ingots;
56
pub mod module;
67
pub mod structs;

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

+13-9
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
}
@@ -334,6 +330,14 @@ pub fn contract_field_type(
334330

335331
let node = &field.data(db).ast;
336332

333+
if let Ok(Type::Trait(ref val)) = typ {
334+
scope.error(
335+
"traits can not be used as contract fields",
336+
node.span,
337+
&format!("trait `{}` can not appear here", val.name),
338+
);
339+
}
340+
337341
if node.kind.is_pub {
338342
scope.not_yet_implemented("contract `pub` fields", node.span);
339343
}

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

+61-16
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,27 @@ 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};
99
use crate::traversal::functions::traverse_statements;
1010
use crate::traversal::types::type_desc;
1111
use fe_common::diagnostics::Label;
12-
use fe_parser::ast;
12+
use fe_parser::ast::{self, GenericParameter};
1313
use fe_parser::node::Node;
1414
use if_chain::if_chain;
15+
use smol_str::SmolStr;
1516
use std::collections::HashMap;
1617
use std::rc::Rc;
1718

1819
/// Gather context information for a function definition and check for type
1920
/// errors. Does not inspect the function body.
2021
pub fn function_signature(
2122
db: &dyn AnalyzerDb,
22-
function: FunctionId,
23+
function: FunctionSigId,
2324
) -> Analysis<Rc<types::FunctionSignature>> {
24-
let node = &function.data(db).ast;
25-
let def = &node.kind;
25+
let def = &function.data(db).ast;
2626

2727
let mut scope = ItemScope::new(db, function.module(db));
2828
let fn_parent = function.class(db);
@@ -32,9 +32,27 @@ pub fn function_signature(
3232
let mut names = HashMap::new();
3333
let mut labels = HashMap::new();
3434

35-
if let (Some(Class::Contract(_)), true) = (fn_parent, function.is_generic(db)) {
35+
function.data(db).ast.kind.generic_params.kind.iter().fold(
36+
HashMap::<SmolStr, Node<_>>::new(),
37+
|mut accum, param| {
38+
if let Some(previous) = accum.get(&param.name()) {
39+
scope.duplicate_name_error(
40+
"duplicate generic parameter",
41+
&param.name(),
42+
previous.span,
43+
param.name_node().span,
44+
);
45+
} else {
46+
accum.insert(param.name(), param.name_node());
47+
};
48+
49+
accum
50+
},
51+
);
52+
53+
if !matches!(fn_parent, Some(Class::Struct(_))) && function.is_generic(db) {
3654
scope.fancy_error(
37-
"generic function parameters aren't yet supported in contract functions",
55+
"generic function parameters aren't yet supported outside of struct functions",
3856
vec![Label::primary(
3957
function.data(db).ast.kind.generic_params.span,
4058
"This can not appear here",
@@ -43,7 +61,26 @@ pub fn function_signature(
4361
);
4462
}
4563

64+
if function.is_generic(db) {
65+
for param in function.data(db).ast.kind.generic_params.kind.iter() {
66+
if let GenericParameter::Unbounded(val) = param {
67+
scope.fancy_error(
68+
"unbounded generic parameters aren't yet supported",
69+
vec![Label::primary(
70+
val.span,
71+
format!("`{}` needs to be bound by some trait", val.kind),
72+
)],
73+
vec![format!(
74+
"Hint: Change `{}` to `{}: SomeTrait`",
75+
val.kind, val.kind
76+
)],
77+
);
78+
}
79+
}
80+
}
81+
4682
let params = def
83+
.kind
4784
.args
4885
.iter()
4986
.enumerate()
@@ -73,7 +110,7 @@ pub fn function_signature(
73110
_ => Err(TypeError::new(scope.error(
74111
"function parameter types must have fixed size",
75112
reg.typ.span,
76-
"`Map` type can't be used as a function parameter",
113+
&format!("`{}` type can't be used as a function parameter", typ.name()),
77114
))),
78115
});
79116

@@ -114,9 +151,9 @@ pub fn function_signature(
114151
if label.kind != "_";
115152
if let Some(dup_idx) = labels.get(&label.kind);
116153
then {
117-
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
154+
let dup_arg: &Node<ast::FunctionArg> = &def.kind.args[*dup_idx];
118155
scope.fancy_error(
119-
&format!("duplicate parameter labels in function `{}`", def.name.kind),
156+
&format!("duplicate parameter labels in function `{}`", def.kind.name.kind),
120157
vec![
121158
Label::primary(dup_arg.span, "the label `{}` was first used here"),
122159
Label::primary(label.span, "label `{}` used again here"),
@@ -138,9 +175,9 @@ pub fn function_signature(
138175
);
139176
None
140177
} else if let Some(dup_idx) = names.get(&reg.name.kind) {
141-
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
178+
let dup_arg: &Node<ast::FunctionArg> = &def.kind.args[*dup_idx];
142179
scope.duplicate_name_error(
143-
&format!("duplicate parameter names in function `{}`", def.name.kind),
180+
&format!("duplicate parameter names in function `{}`", function.name(db)),
144181
&reg.name.kind,
145182
dup_arg.span,
146183
arg.span,
@@ -159,10 +196,11 @@ pub fn function_signature(
159196
.collect();
160197

161198
let return_type = def
199+
.kind
162200
.return_type
163201
.as_ref()
164202
.map(|type_node| {
165-
let fn_name = &def.name.kind;
203+
let fn_name = &function.name(db);
166204
if fn_name == "__init__" || fn_name == "__call__" {
167205
// `__init__` and `__call__` must not return any type other than `()`.
168206
if type_node.kind != ast::TypeDesc::Unit {
@@ -207,7 +245,7 @@ pub fn function_signature(
207245

208246
pub fn resolve_function_param_type(
209247
db: &dyn AnalyzerDb,
210-
function: FunctionId,
248+
function: FunctionSigId,
211249
context: &mut dyn AnalyzerContext,
212250
desc: &Node<ast::TypeDesc>,
213251
) -> Result<Type, TypeError> {
@@ -254,11 +292,11 @@ pub fn function_body(db: &dyn AnalyzerDb, function: FunctionId) -> Analysis<Rc<F
254292
"function body is missing a return or revert statement",
255293
vec![
256294
Label::primary(
257-
def.name.span,
295+
function.name_span(db),
258296
"all paths of this function must `return` or `revert`",
259297
),
260298
Label::secondary(
261-
def.return_type.as_ref().unwrap().span,
299+
def.sig.kind.return_type.as_ref().unwrap().span,
262300
format!("expected function to return `{}`", return_type),
263301
),
264302
],
@@ -362,6 +400,13 @@ pub fn function_dependency_graph(db: &dyn AnalyzerDb, function: FunctionId) -> D
362400
directs.push((root, class.as_item(), DepLocality::Local));
363401
directs.push((root, Item::Function(*method), DepLocality::Local));
364402
}
403+
CallType::TraitValueMethod { trait_id, .. } => {
404+
directs.push((
405+
root,
406+
Item::Type(TypeDef::Trait(*trait_id)),
407+
DepLocality::Local,
408+
));
409+
}
365410
CallType::External { contract, function } => {
366411
directs.push((root, Item::Function(*function), DepLocality::External));
367412
// Probably redundant:

0 commit comments

Comments
 (0)