Skip to content

Commit a858bdb

Browse files
committed
WIP
1 parent 79e52e4 commit a858bdb

33 files changed

+527
-106
lines changed

crates/analyzer/src/context.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::context::{Analysis, Constant, FunctionBody};
22
use crate::errors::{ConstEvalError, TypeError};
33
use crate::namespace::items::{
44
self, ContractFieldId, ContractId, DepGraphWrapper, EventId, FunctionId, IngotId, Item,
5-
ModuleConstantId, ModuleId, StructFieldId, StructId, TypeAliasId,
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;
@@ -154,6 +156,10 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
154156
#[salsa::invoke(queries::structs::struct_dependency_graph)]
155157
fn struct_dependency_graph(&self, id: StructId) -> Analysis<DepGraphWrapper>;
156158

159+
// Trait
160+
#[salsa::invoke(queries::traits::trait_type)]
161+
fn trait_type(&self, id: TraitId) -> Rc<types::Trait>;
162+
157163
// Event
158164
#[salsa::invoke(queries::events::event_type)]
159165
fn event_type(&self, event: EventId) -> Analysis<Rc<types::Event>>;

crates/analyzer/src/db/queries.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
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;
10-
use fe_parser::ast;
12+
use fe_parser::ast::{self, GenericParameter};
1113
use fe_parser::node::Node;
1214
use if_chain::if_chain;
1315
use std::collections::HashMap;
@@ -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. This may not hold when generics can appear
215+
// on contract, struct or module level but it's good enough to get the party started.
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+
GenericParameter::Unbounded(_) => vec![],
220+
GenericParameter::Bounded { bound, .. } => match type_desc(context, &bound)? {
221+
Type::Trait(trait_ty) => vec![trait_ty.id],
222+
_ => {
223+
context.error(
224+
"Only traits can be used as generic bounds",
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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::db::AnalyzerDb;
33
use crate::errors::{self, ConstEvalError, TypeError};
44
use crate::namespace::items::{
55
Contract, ContractId, Event, Function, Item, ModuleConstant, ModuleConstantId, ModuleId,
6-
ModuleSource, Struct, StructId, TypeAlias, TypeDef,
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(
@@ -95,7 +94,12 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
9594
}))))
9695
}
9796
ast::ModuleStmt::Pragma(_) => None,
98-
ast::ModuleStmt::Trait(_) => None,
97+
ast::ModuleStmt::Trait(node) => Some(Item::Type(TypeDef::Trait(db.intern_trait(
98+
Rc::new(Trait {
99+
ast: node.clone(),
100+
module,
101+
}),
102+
)))),
99103
ast::ModuleStmt::Use(_) => None,
100104
ast::ModuleStmt::Event(node) => Some(Item::Event(db.intern_event(Rc::new(Event {
101105
ast: node.clone(),
Lines changed: 11 additions & 0 deletions
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+
}

crates/analyzer/src/namespace/items.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{builtins, errors::ConstEvalError};
99
use fe_common::diagnostics::Diagnostic;
1010
use fe_common::files::{common_prefix, Utf8Path};
1111
use fe_common::{impl_intern_key, FileKind, SourceFileId};
12+
use fe_parser::ast::GenericParameter;
1213
use fe_parser::node::{Node, Span};
1314
use fe_parser::{ast, node::NodeId};
1415
use indexmap::{indexmap, IndexMap};
@@ -469,6 +470,10 @@ impl ModuleId {
469470
db.module_is_incomplete(*self)
470471
}
471472

473+
pub fn is_in_std(&self, db: &dyn AnalyzerDb) -> bool {
474+
self.ingot(db).name(db) == "std"
475+
}
476+
472477
/// Includes duplicate names
473478
pub fn all_items(&self, db: &dyn AnalyzerDb) -> Rc<[Item]> {
474479
db.module_all_items(*self)
@@ -747,6 +752,7 @@ impl ModuleConstantId {
747752
pub enum TypeDef {
748753
Alias(TypeAliasId),
749754
Struct(StructId),
755+
Trait(TraitId),
750756
Contract(ContractId),
751757
Primitive(types::Base),
752758
}
@@ -778,6 +784,7 @@ impl TypeDef {
778784
match self {
779785
TypeDef::Alias(id) => id.name(db),
780786
TypeDef::Struct(id) => id.name(db),
787+
TypeDef::Trait(id) => id.name(db),
781788
TypeDef::Contract(id) => id.name(db),
782789
TypeDef::Primitive(typ) => typ.name(),
783790
}
@@ -787,6 +794,7 @@ impl TypeDef {
787794
match self {
788795
TypeDef::Alias(id) => Some(id.name_span(db)),
789796
TypeDef::Struct(id) => Some(id.name_span(db)),
797+
TypeDef::Trait(id) => Some(id.name_span(db)),
790798
TypeDef::Contract(id) => Some(id.name_span(db)),
791799
TypeDef::Primitive(_) => None,
792800
}
@@ -800,6 +808,10 @@ impl TypeDef {
800808
name: id.name(db),
801809
field_count: id.fields(db).len(), // for the EvmSized trait
802810
})),
811+
TypeDef::Trait(id) => Ok(types::Type::Trait(types::Trait {
812+
id: *id,
813+
name: id.name(db),
814+
})),
803815
TypeDef::Contract(id) => Ok(types::Type::Contract(types::Contract {
804816
id: *id,
805817
name: id.name(db),
@@ -812,6 +824,7 @@ impl TypeDef {
812824
match self {
813825
Self::Alias(id) => id.is_public(db),
814826
Self::Struct(id) => id.is_public(db),
827+
Self::Trait(id) => id.is_public(db),
815828
Self::Contract(id) => id.is_public(db),
816829
Self::Primitive(_) => true,
817830
}
@@ -821,6 +834,7 @@ impl TypeDef {
821834
match self {
822835
TypeDef::Alias(id) => Some(id.parent(db)),
823836
TypeDef::Struct(id) => Some(id.parent(db)),
837+
TypeDef::Trait(id) => Some(id.parent(db)),
824838
TypeDef::Contract(id) => Some(id.parent(db)),
825839
TypeDef::Primitive(_) => None,
826840
}
@@ -830,6 +844,7 @@ impl TypeDef {
830844
match self {
831845
TypeDef::Alias(id) => id.sink_diagnostics(db, sink),
832846
TypeDef::Struct(id) => id.sink_diagnostics(db, sink),
847+
TypeDef::Trait(id) => id.sink_diagnostics(db, sink),
833848
TypeDef::Contract(id) => id.sink_diagnostics(db, sink),
834849
TypeDef::Primitive(_) => {}
835850
}
@@ -1097,6 +1112,7 @@ impl FunctionId {
10971112
pub fn takes_self(&self, db: &dyn AnalyzerDb) -> bool {
10981113
self.signature(db).self_decl.is_some()
10991114
}
1115+
11001116
pub fn self_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
11011117
if self.takes_self(db) {
11021118
self.data(db)
@@ -1110,6 +1126,25 @@ impl FunctionId {
11101126
}
11111127
}
11121128

1129+
pub fn is_generic(&self, db: &dyn AnalyzerDb) -> bool {
1130+
!self.data(db).ast.kind.generic_params.kind.is_empty()
1131+
}
1132+
1133+
pub fn generic_params(&self, db: &dyn AnalyzerDb) -> Vec<GenericParameter> {
1134+
self.data(db).ast.kind.generic_params.kind.clone()
1135+
}
1136+
1137+
pub fn generic_param(&self, db: &dyn AnalyzerDb, param_name: &str) -> Option<GenericParameter> {
1138+
self.generic_params(db)
1139+
.iter()
1140+
.find(|param| match param {
1141+
GenericParameter::Unbounded(name) if name.kind == param_name => true,
1142+
GenericParameter::Bounded { name, .. } if name.kind == param_name => true,
1143+
_ => false,
1144+
})
1145+
.cloned()
1146+
}
1147+
11131148
pub fn is_public(&self, db: &dyn AnalyzerDb) -> bool {
11141149
self.pub_span(db).is_some()
11151150
}
@@ -1324,6 +1359,50 @@ impl StructFieldId {
13241359
}
13251360
}
13261361

1362+
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
1363+
pub struct Trait {
1364+
pub ast: Node<ast::Trait>,
1365+
pub module: ModuleId,
1366+
}
1367+
1368+
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
1369+
pub struct TraitId(pub(crate) u32);
1370+
impl_intern_key!(TraitId);
1371+
impl TraitId {
1372+
pub fn data(&self, db: &dyn AnalyzerDb) -> Rc<Trait> {
1373+
db.lookup_intern_trait(*self)
1374+
}
1375+
pub fn span(&self, db: &dyn AnalyzerDb) -> Span {
1376+
self.data(db).ast.span
1377+
}
1378+
pub fn name(&self, db: &dyn AnalyzerDb) -> SmolStr {
1379+
self.data(db).ast.name().into()
1380+
}
1381+
pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span {
1382+
self.data(db).ast.kind.name.span
1383+
}
1384+
1385+
pub fn is_public(&self, db: &dyn AnalyzerDb) -> bool {
1386+
self.data(db).ast.kind.pub_qual.is_some()
1387+
}
1388+
1389+
pub fn module(&self, db: &dyn AnalyzerDb) -> ModuleId {
1390+
self.data(db).module
1391+
}
1392+
1393+
pub fn typ(&self, db: &dyn AnalyzerDb) -> Rc<types::Trait> {
1394+
db.trait_type(*self)
1395+
}
1396+
pub fn is_in_std(&self, db: &dyn AnalyzerDb) -> bool {
1397+
self.module(db).is_in_std(db)
1398+
}
1399+
1400+
pub fn parent(&self, db: &dyn AnalyzerDb) -> Item {
1401+
Item::Module(self.data(db).module)
1402+
}
1403+
pub fn sink_diagnostics(&self, _db: &dyn AnalyzerDb, _sink: &mut impl DiagnosticSink) {}
1404+
}
1405+
13271406
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
13281407
pub struct Event {
13291408
pub ast: Node<ast::Event>,

0 commit comments

Comments
 (0)