Skip to content

Commit dee378a

Browse files
committed
First bits of traits and generic function params
1 parent a761022 commit dee378a

39 files changed

+795
-62
lines changed

crates/analyzer/src/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ impl CallType {
508508
// check that this is the `Context` struct defined in `std`
509509
// this should be deleted once associated functions are supported and we can
510510
// define unsafe constructors in Fe
511-
struct_.name == "Context" && struct_.id.module(db).ingot(db).name(db) == "std"
511+
struct_.name == "Context" && struct_.id.module(db).is_in_std(db)
512512
} else {
513513
self.function().map(|id| id.is_unsafe(db)).unwrap_or(false)
514514
}

crates/analyzer/src/db.rs

+10-2
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, IngotId, Item,
5-
ModuleConstantId, ModuleId, StructFieldId, StructId, TypeAliasId,
4+
self, ContractFieldId, ContractId, DepGraphWrapper, EventId, FunctionId, Impl, IngotId, Item,
5+
ModuleConstantId, ModuleId, StructFieldId, StructId, TraitId, TypeAliasId,
66
};
77
use crate::namespace::types;
88
use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut};
@@ -24,6 +24,8 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
2424
#[salsa::interned]
2525
fn intern_struct(&self, data: Rc<items::Struct>) -> StructId;
2626
#[salsa::interned]
27+
fn intern_trait(&self, data: Rc<items::Trait>) -> TraitId;
28+
#[salsa::interned]
2729
fn intern_struct_field(&self, data: Rc<items::StructField>) -> StructFieldId;
2830
#[salsa::interned]
2931
fn intern_type_alias(&self, data: Rc<items::TypeAlias>) -> TypeAliasId;
@@ -60,6 +62,8 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
6062
fn module_is_incomplete(&self, module: ModuleId) -> bool;
6163
#[salsa::invoke(queries::module::module_all_items)]
6264
fn module_all_items(&self, module: ModuleId) -> Rc<[Item]>;
65+
#[salsa::invoke(queries::module::module_all_impls)]
66+
fn module_all_impls(&self, module: ModuleId) -> Rc<[Impl]>;
6367
#[salsa::invoke(queries::module::module_item_map)]
6468
fn module_item_map(&self, module: ModuleId) -> Analysis<Rc<IndexMap<SmolStr, Item>>>;
6569
#[salsa::invoke(queries::module::module_contracts)]
@@ -154,6 +158,10 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
154158
#[salsa::invoke(queries::structs::struct_dependency_graph)]
155159
fn struct_dependency_graph(&self, id: StructId) -> Analysis<DepGraphWrapper>;
156160

161+
// Trait
162+
#[salsa::invoke(queries::traits::trait_type)]
163+
fn trait_type(&self, id: TraitId) -> Rc<types::Trait>;
164+
157165
// Event
158166
#[salsa::invoke(queries::events::event_type)]
159167
fn event_type(&self, event: EventId) -> Analysis<Rc<types::Event>>;

crates/analyzer/src/db/queries.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ pub mod functions;
44
pub mod ingots;
55
pub mod module;
66
pub mod structs;
7+
pub mod traits;
78
pub mod types;

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

+50-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use crate::context::{AnalyzerContext, CallType, FunctionBody};
22
use crate::db::{Analysis, AnalyzerDb};
33
use crate::errors::TypeError;
4-
use crate::namespace::items::{DepGraph, DepGraphWrapper, DepLocality, FunctionId, Item, TypeDef};
4+
use crate::namespace::items::{
5+
Class, DepGraph, DepGraphWrapper, DepLocality, FunctionId, Item, TypeDef,
6+
};
57
use crate::namespace::scopes::{BlockScope, BlockScopeType, FunctionScope, ItemScope};
6-
use crate::namespace::types::{self, Contract, CtxDecl, SelfDecl, Struct, Type};
8+
use crate::namespace::types::{self, Contract, CtxDecl, Generic, SelfDecl, Struct, Type};
79
use crate::traversal::functions::traverse_statements;
810
use crate::traversal::types::type_desc;
911
use fe_common::diagnostics::Label;
@@ -30,6 +32,17 @@ pub fn function_signature(
3032
let mut names = HashMap::new();
3133
let mut labels = HashMap::new();
3234

35+
if let (Some(Class::Contract(_)), true) = (fn_parent, function.is_generic(db)) {
36+
scope.fancy_error(
37+
"generic function parameters aren't yet supported in contract functions",
38+
vec![Label::primary(
39+
function.data(db).ast.kind.generic_params.span,
40+
"This can not appear here",
41+
)],
42+
vec!["Hint: Struct functions can have generic parameters".into()],
43+
);
44+
}
45+
3346
let params = def
3447
.args
3548
.iter()
@@ -55,7 +68,7 @@ pub fn function_signature(
5568
None
5669
}
5770
ast::FunctionArg::Regular(reg) => {
58-
let typ = type_desc(&mut scope, &reg.typ).and_then(|typ| match typ {
71+
let typ = resolve_function_param_type(db, function, &mut scope, &reg.typ).and_then(|typ| match typ {
5972
typ if typ.has_fixed_size() => Ok(typ),
6073
_ => Err(TypeError::new(scope.error(
6174
"function parameter types must have fixed size",
@@ -192,6 +205,40 @@ pub fn function_signature(
192205
}
193206
}
194207

208+
pub fn resolve_function_param_type(
209+
db: &dyn AnalyzerDb,
210+
function: FunctionId,
211+
context: &mut dyn AnalyzerContext,
212+
desc: &Node<ast::TypeDesc>,
213+
) -> Result<Type, TypeError> {
214+
// First check if the param type is a local generic of the function. This won't hold when in the future
215+
// generics can appear on the contract, struct or module level but it could be good enough for now.
216+
if let ast::TypeDesc::Base { base } = &desc.kind {
217+
if let Some(val) = function.generic_param(db, base) {
218+
let bounds = match val {
219+
ast::GenericParameter::Unbounded(_) => vec![],
220+
ast::GenericParameter::Bounded { bound, .. } => match type_desc(context, &bound)? {
221+
Type::Trait(trait_ty) => vec![trait_ty.id],
222+
other => {
223+
context.error(
224+
&format!("expected trait, found type `{}`", other),
225+
bound.span,
226+
"not a trait",
227+
);
228+
vec![]
229+
}
230+
},
231+
};
232+
233+
return Ok(Type::Generic(Generic {
234+
name: base.clone(),
235+
bounds,
236+
}));
237+
}
238+
}
239+
type_desc(context, desc)
240+
}
241+
195242
/// Gather context information for a function body and check for type errors.
196243
pub fn function_body(db: &dyn AnalyzerDb, function: FunctionId) -> Analysis<Rc<FunctionBody>> {
197244
let def = &function.data(db).ast.kind;

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

+38-5
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, Item, ModuleConstant, ModuleConstantId, ModuleId,
6-
ModuleSource, Struct, StructId, TypeAlias, TypeDef,
5+
Contract, ContractId, Event, Function, Impl, Item, ModuleConstant, ModuleConstantId, ModuleId,
6+
ModuleSource, Struct, StructId, Trait, TypeAlias, TypeDef,
77
};
88
use crate::namespace::scopes::ItemScope;
99
use crate::namespace::types::{self, Type};
@@ -59,7 +59,6 @@ pub fn module_is_incomplete(db: &dyn AnalyzerDb, module: ModuleId) -> bool {
5959

6060
pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
6161
let body = &module.ast(db).body;
62-
6362
body.iter()
6463
.filter_map(|stmt| match stmt {
6564
ast::ModuleStmt::TypeAlias(node) => Some(Item::Type(TypeDef::Alias(
@@ -94,8 +93,13 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
9493
parent: None,
9594
}))))
9695
}
97-
ast::ModuleStmt::Pragma(_) => None,
98-
ast::ModuleStmt::Use(_) => None,
96+
ast::ModuleStmt::Trait(node) => Some(Item::Type(TypeDef::Trait(db.intern_trait(
97+
Rc::new(Trait {
98+
ast: node.clone(),
99+
module,
100+
}),
101+
)))),
102+
ast::ModuleStmt::Pragma(_) | ast::ModuleStmt::Use(_) | ast::ModuleStmt::Impl(_) => None,
99103
ast::ModuleStmt::Event(node) => Some(Item::Event(db.intern_event(Rc::new(Event {
100104
ast: node.clone(),
101105
module,
@@ -106,6 +110,35 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
106110
.collect()
107111
}
108112

113+
pub fn module_all_impls(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Impl]> {
114+
let body = &module.ast(db).body;
115+
body.iter()
116+
.filter_map(|stmt| match stmt {
117+
ast::ModuleStmt::Impl(impl_node) => {
118+
let treit = module
119+
.items(db)
120+
.get(&impl_node.kind.impl_trait.kind)
121+
.cloned();
122+
123+
let mut scope = ItemScope::new(db, module);
124+
let receiver_type = type_desc(&mut scope, &impl_node.kind.receiver).unwrap();
125+
126+
if let Some(Item::Type(TypeDef::Trait(val))) = treit {
127+
Some(Impl {
128+
trait_id: val,
129+
receiver: receiver_type,
130+
ast: impl_node.clone(),
131+
module,
132+
})
133+
} else {
134+
None
135+
}
136+
}
137+
_ => None,
138+
})
139+
.collect()
140+
}
141+
109142
pub fn module_item_map(
110143
db: &dyn AnalyzerDb,
111144
module: ModuleId,
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use crate::namespace::items::TraitId;
2+
use crate::namespace::types;
3+
use crate::AnalyzerDb;
4+
use std::rc::Rc;
5+
6+
pub fn trait_type(db: &dyn AnalyzerDb, trait_: TraitId) -> Rc<types::Trait> {
7+
Rc::new(types::Trait {
8+
name: trait_.name(db),
9+
id: trait_,
10+
})
11+
}

0 commit comments

Comments
 (0)