Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed Jun 3, 2022
1 parent 79e52e4 commit a858bdb
Show file tree
Hide file tree
Showing 33 changed files with 527 additions and 106 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
8 changes: 7 additions & 1 deletion crates/analyzer/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ 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,
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 @@ -154,6 +156,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;
55 changes: 51 additions & 4 deletions crates/analyzer/src/db/queries/functions.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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;
use fe_parser::ast;
use fe_parser::ast::{self, GenericParameter};
use fe_parser::node::Node;
use if_chain::if_chain;
use std::collections::HashMap;
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. This may not hold when generics can appear
// on contract, struct or module level but it's good enough to get the party started.
if let ast::TypeDesc::Base { base } = &desc.kind {
if let Some(val) = function.generic_param(db, base) {
let bounds = match val {
GenericParameter::Unbounded(_) => vec![],
GenericParameter::Bounded { bound, .. } => match type_desc(context, &bound)? {
Type::Trait(trait_ty) => vec![trait_ty.id],
_ => {
context.error(
"Only traits can be used as generic bounds",
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
10 changes: 7 additions & 3 deletions crates/analyzer/src/db/queries/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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,
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 @@ -95,7 +94,12 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
}))))
}
ast::ModuleStmt::Pragma(_) => None,
ast::ModuleStmt::Trait(_) => None,
ast::ModuleStmt::Trait(node) => Some(Item::Type(TypeDef::Trait(db.intern_trait(
Rc::new(Trait {
ast: node.clone(),
module,
}),
)))),
ast::ModuleStmt::Use(_) => None,
ast::ModuleStmt::Event(node) => Some(Item::Event(db.intern_event(Rc::new(Event {
ast: node.clone(),
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_,
})
}
79 changes: 79 additions & 0 deletions crates/analyzer/src/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{builtins, errors::ConstEvalError};
use fe_common::diagnostics::Diagnostic;
use fe_common::files::{common_prefix, Utf8Path};
use fe_common::{impl_intern_key, FileKind, SourceFileId};
use fe_parser::ast::GenericParameter;
use fe_parser::node::{Node, Span};
use fe_parser::{ast, node::NodeId};
use indexmap::{indexmap, IndexMap};
Expand Down Expand Up @@ -469,6 +470,10 @@ impl ModuleId {
db.module_is_incomplete(*self)
}

pub fn is_in_std(&self, db: &dyn AnalyzerDb) -> bool {
self.ingot(db).name(db) == "std"
}

/// Includes duplicate names
pub fn all_items(&self, db: &dyn AnalyzerDb) -> Rc<[Item]> {
db.module_all_items(*self)
Expand Down Expand Up @@ -747,6 +752,7 @@ impl ModuleConstantId {
pub enum TypeDef {
Alias(TypeAliasId),
Struct(StructId),
Trait(TraitId),
Contract(ContractId),
Primitive(types::Base),
}
Expand Down Expand Up @@ -778,6 +784,7 @@ impl TypeDef {
match self {
TypeDef::Alias(id) => id.name(db),
TypeDef::Struct(id) => id.name(db),
TypeDef::Trait(id) => id.name(db),
TypeDef::Contract(id) => id.name(db),
TypeDef::Primitive(typ) => typ.name(),
}
Expand All @@ -787,6 +794,7 @@ impl TypeDef {
match self {
TypeDef::Alias(id) => Some(id.name_span(db)),
TypeDef::Struct(id) => Some(id.name_span(db)),
TypeDef::Trait(id) => Some(id.name_span(db)),
TypeDef::Contract(id) => Some(id.name_span(db)),
TypeDef::Primitive(_) => None,
}
Expand All @@ -800,6 +808,10 @@ impl TypeDef {
name: id.name(db),
field_count: id.fields(db).len(), // for the EvmSized trait
})),
TypeDef::Trait(id) => Ok(types::Type::Trait(types::Trait {
id: *id,
name: id.name(db),
})),
TypeDef::Contract(id) => Ok(types::Type::Contract(types::Contract {
id: *id,
name: id.name(db),
Expand All @@ -812,6 +824,7 @@ impl TypeDef {
match self {
Self::Alias(id) => id.is_public(db),
Self::Struct(id) => id.is_public(db),
Self::Trait(id) => id.is_public(db),
Self::Contract(id) => id.is_public(db),
Self::Primitive(_) => true,
}
Expand All @@ -821,6 +834,7 @@ impl TypeDef {
match self {
TypeDef::Alias(id) => Some(id.parent(db)),
TypeDef::Struct(id) => Some(id.parent(db)),
TypeDef::Trait(id) => Some(id.parent(db)),
TypeDef::Contract(id) => Some(id.parent(db)),
TypeDef::Primitive(_) => None,
}
Expand All @@ -830,6 +844,7 @@ impl TypeDef {
match self {
TypeDef::Alias(id) => id.sink_diagnostics(db, sink),
TypeDef::Struct(id) => id.sink_diagnostics(db, sink),
TypeDef::Trait(id) => id.sink_diagnostics(db, sink),
TypeDef::Contract(id) => id.sink_diagnostics(db, sink),
TypeDef::Primitive(_) => {}
}
Expand Down Expand Up @@ -1097,6 +1112,7 @@ impl FunctionId {
pub fn takes_self(&self, db: &dyn AnalyzerDb) -> bool {
self.signature(db).self_decl.is_some()
}

pub fn self_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
if self.takes_self(db) {
self.data(db)
Expand All @@ -1110,6 +1126,25 @@ impl FunctionId {
}
}

pub fn is_generic(&self, db: &dyn AnalyzerDb) -> bool {
!self.data(db).ast.kind.generic_params.kind.is_empty()
}

pub fn generic_params(&self, db: &dyn AnalyzerDb) -> Vec<GenericParameter> {
self.data(db).ast.kind.generic_params.kind.clone()
}

pub fn generic_param(&self, db: &dyn AnalyzerDb, param_name: &str) -> Option<GenericParameter> {
self.generic_params(db)
.iter()
.find(|param| match param {
GenericParameter::Unbounded(name) if name.kind == param_name => true,
GenericParameter::Bounded { name, .. } if name.kind == param_name => true,
_ => false,
})
.cloned()
}

pub fn is_public(&self, db: &dyn AnalyzerDb) -> bool {
self.pub_span(db).is_some()
}
Expand Down Expand Up @@ -1324,6 +1359,50 @@ impl StructFieldId {
}
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Trait {
pub ast: Node<ast::Trait>,
pub module: ModuleId,
}

#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
pub struct TraitId(pub(crate) u32);
impl_intern_key!(TraitId);
impl TraitId {
pub fn data(&self, db: &dyn AnalyzerDb) -> Rc<Trait> {
db.lookup_intern_trait(*self)
}
pub fn span(&self, db: &dyn AnalyzerDb) -> Span {
self.data(db).ast.span
}
pub fn name(&self, db: &dyn AnalyzerDb) -> SmolStr {
self.data(db).ast.name().into()
}
pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span {
self.data(db).ast.kind.name.span
}

pub fn is_public(&self, db: &dyn AnalyzerDb) -> bool {
self.data(db).ast.kind.pub_qual.is_some()
}

pub fn module(&self, db: &dyn AnalyzerDb) -> ModuleId {
self.data(db).module
}

pub fn typ(&self, db: &dyn AnalyzerDb) -> Rc<types::Trait> {
db.trait_type(*self)
}
pub fn is_in_std(&self, db: &dyn AnalyzerDb) -> bool {
self.module(db).is_in_std(db)
}

pub fn parent(&self, db: &dyn AnalyzerDb) -> Item {
Item::Module(self.data(db).module)
}
pub fn sink_diagnostics(&self, _db: &dyn AnalyzerDb, _sink: &mut impl DiagnosticSink) {}
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Event {
pub ast: Node<ast::Event>,
Expand Down
Loading

0 comments on commit a858bdb

Please sign in to comment.