Skip to content

Commit

Permalink
First bits of traits and generic function params
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed Jun 16, 2022
1 parent 471714f commit 022a17b
Show file tree
Hide file tree
Showing 39 changed files with 795 additions and 62 deletions.
2 changes: 1 addition & 1 deletion crates/analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ impl CallType {
// check that this is the `Context` struct defined in `std`
// this should be deleted once associated functions are supported and we can
// define unsafe constructors in Fe
struct_.name == "Context" && struct_.id.module(db).ingot(db).name(db) == "std"
struct_.name == "Context" && struct_.id.module(db).is_in_std(db)
} else {
self.function().map(|id| id.is_unsafe(db)).unwrap_or(false)
}
Expand Down
12 changes: 10 additions & 2 deletions crates/analyzer/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::context::{Analysis, Constant, FunctionBody};
use crate::errors::{ConstEvalError, TypeError};
use crate::namespace::items::{
self, ContractFieldId, ContractId, DepGraphWrapper, EventId, FunctionId, IngotId, Item,
ModuleConstantId, ModuleId, StructFieldId, StructId, TypeAliasId,
self, ContractFieldId, ContractId, DepGraphWrapper, EventId, FunctionId, Impl, IngotId, Item,
ModuleConstantId, ModuleId, StructFieldId, StructId, TraitId, TypeAliasId,
};
use crate::namespace::types;
use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut};
Expand All @@ -24,6 +24,8 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
#[salsa::interned]
fn intern_struct(&self, data: Rc<items::Struct>) -> StructId;
#[salsa::interned]
fn intern_trait(&self, data: Rc<items::Trait>) -> TraitId;
#[salsa::interned]
fn intern_struct_field(&self, data: Rc<items::StructField>) -> StructFieldId;
#[salsa::interned]
fn intern_type_alias(&self, data: Rc<items::TypeAlias>) -> TypeAliasId;
Expand Down Expand Up @@ -60,6 +62,8 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
fn module_is_incomplete(&self, module: ModuleId) -> bool;
#[salsa::invoke(queries::module::module_all_items)]
fn module_all_items(&self, module: ModuleId) -> Rc<[Item]>;
#[salsa::invoke(queries::module::module_all_impls)]
fn module_all_impls(&self, module: ModuleId) -> Rc<[Impl]>;
#[salsa::invoke(queries::module::module_item_map)]
fn module_item_map(&self, module: ModuleId) -> Analysis<Rc<IndexMap<SmolStr, Item>>>;
#[salsa::invoke(queries::module::module_contracts)]
Expand Down Expand Up @@ -154,6 +158,10 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
#[salsa::invoke(queries::structs::struct_dependency_graph)]
fn struct_dependency_graph(&self, id: StructId) -> Analysis<DepGraphWrapper>;

// Trait
#[salsa::invoke(queries::traits::trait_type)]
fn trait_type(&self, id: TraitId) -> Rc<types::Trait>;

// Event
#[salsa::invoke(queries::events::event_type)]
fn event_type(&self, event: EventId) -> Analysis<Rc<types::Event>>;
Expand Down
1 change: 1 addition & 0 deletions crates/analyzer/src/db/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pub mod functions;
pub mod ingots;
pub mod module;
pub mod structs;
pub mod traits;
pub mod types;
53 changes: 50 additions & 3 deletions crates/analyzer/src/db/queries/functions.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::context::{AnalyzerContext, CallType, FunctionBody};
use crate::db::{Analysis, AnalyzerDb};
use crate::errors::TypeError;
use crate::namespace::items::{DepGraph, DepGraphWrapper, DepLocality, FunctionId, Item, TypeDef};
use crate::namespace::items::{
Class, DepGraph, DepGraphWrapper, DepLocality, FunctionId, Item, TypeDef,
};
use crate::namespace::scopes::{BlockScope, BlockScopeType, FunctionScope, ItemScope};
use crate::namespace::types::{self, Contract, CtxDecl, SelfDecl, Struct, Type};
use crate::namespace::types::{self, Contract, CtxDecl, Generic, SelfDecl, Struct, Type};
use crate::traversal::functions::traverse_statements;
use crate::traversal::types::type_desc;
use fe_common::diagnostics::Label;
Expand All @@ -30,6 +32,17 @@ pub fn function_signature(
let mut names = HashMap::new();
let mut labels = HashMap::new();

if let (Some(Class::Contract(_)), true) = (fn_parent, function.is_generic(db)) {
scope.fancy_error(
"generic function parameters aren't yet supported in contract functions",
vec![Label::primary(
function.data(db).ast.kind.generic_params.span,
"This can not appear here",
)],
vec!["Hint: Struct functions can have generic parameters".into()],
);
}

let params = def
.args
.iter()
Expand All @@ -55,7 +68,7 @@ pub fn function_signature(
None
}
ast::FunctionArg::Regular(reg) => {
let typ = type_desc(&mut scope, &reg.typ).and_then(|typ| match typ {
let typ = resolve_function_param_type(db, function, &mut scope, &reg.typ).and_then(|typ| match typ {
typ if typ.has_fixed_size() => Ok(typ),
_ => Err(TypeError::new(scope.error(
"function parameter types must have fixed size",
Expand Down Expand Up @@ -192,6 +205,40 @@ pub fn function_signature(
}
}

pub fn resolve_function_param_type(
db: &dyn AnalyzerDb,
function: FunctionId,
context: &mut dyn AnalyzerContext,
desc: &Node<ast::TypeDesc>,
) -> Result<Type, TypeError> {
// First check if the param type is a local generic of the function. This won't hold when in the future
// generics can appear on the contract, struct or module level but it could be good enough for now.
if let ast::TypeDesc::Base { base } = &desc.kind {
if let Some(val) = function.generic_param(db, base) {
let bounds = match val {
ast::GenericParameter::Unbounded(_) => vec![],
ast::GenericParameter::Bounded { bound, .. } => match type_desc(context, &bound)? {
Type::Trait(trait_ty) => vec![trait_ty.id],
other => {
context.error(
&format!("expected trait, found type `{}`", other),
bound.span,
"not a trait",
);
vec![]
}
},
};

return Ok(Type::Generic(Generic {
name: base.clone(),
bounds,
}));
}
}
type_desc(context, desc)
}

/// Gather context information for a function body and check for type errors.
pub fn function_body(db: &dyn AnalyzerDb, function: FunctionId) -> Analysis<Rc<FunctionBody>> {
let def = &function.data(db).ast.kind;
Expand Down
43 changes: 38 additions & 5 deletions crates/analyzer/src/db/queries/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::context::{Analysis, AnalyzerContext, Constant};
use crate::db::AnalyzerDb;
use crate::errors::{self, ConstEvalError, TypeError};
use crate::namespace::items::{
Contract, ContractId, Event, Function, Item, ModuleConstant, ModuleConstantId, ModuleId,
ModuleSource, Struct, StructId, TypeAlias, TypeDef,
Contract, ContractId, Event, Function, Impl, Item, ModuleConstant, ModuleConstantId, ModuleId,
ModuleSource, Struct, StructId, Trait, TypeAlias, TypeDef,
};
use crate::namespace::scopes::ItemScope;
use crate::namespace::types::{self, Type};
Expand Down Expand Up @@ -59,7 +59,6 @@ pub fn module_is_incomplete(db: &dyn AnalyzerDb, module: ModuleId) -> bool {

pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
let body = &module.ast(db).body;

body.iter()
.filter_map(|stmt| match stmt {
ast::ModuleStmt::TypeAlias(node) => Some(Item::Type(TypeDef::Alias(
Expand Down Expand Up @@ -94,8 +93,13 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
parent: None,
}))))
}
ast::ModuleStmt::Pragma(_) => None,
ast::ModuleStmt::Use(_) => None,
ast::ModuleStmt::Trait(node) => Some(Item::Type(TypeDef::Trait(db.intern_trait(
Rc::new(Trait {
ast: node.clone(),
module,
}),
)))),
ast::ModuleStmt::Pragma(_) | ast::ModuleStmt::Use(_) | ast::ModuleStmt::Impl(_) => None,
ast::ModuleStmt::Event(node) => Some(Item::Event(db.intern_event(Rc::new(Event {
ast: node.clone(),
module,
Expand All @@ -106,6 +110,35 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
.collect()
}

pub fn module_all_impls(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Impl]> {
let body = &module.ast(db).body;
body.iter()
.filter_map(|stmt| match stmt {
ast::ModuleStmt::Impl(impl_node) => {
let treit = module
.items(db)
.get(&impl_node.kind.impl_trait.kind)
.cloned();

let mut scope = ItemScope::new(db, module);
let receiver_type = type_desc(&mut scope, &impl_node.kind.receiver).unwrap();

if let Some(Item::Type(TypeDef::Trait(val))) = treit {
Some(Impl {
trait_id: val,
receiver: receiver_type,
ast: impl_node.clone(),
module,
})
} else {
None
}
}
_ => None,
})
.collect()
}

pub fn module_item_map(
db: &dyn AnalyzerDb,
module: ModuleId,
Expand Down
11 changes: 11 additions & 0 deletions crates/analyzer/src/db/queries/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use crate::namespace::items::TraitId;
use crate::namespace::types;
use crate::AnalyzerDb;
use std::rc::Rc;

pub fn trait_type(db: &dyn AnalyzerDb, trait_: TraitId) -> Rc<types::Trait> {
Rc::new(types::Trait {
name: trait_.name(db),
id: trait_,
})
}
Loading

0 comments on commit 022a17b

Please sign in to comment.