Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sbillig committed Jul 8, 2021
1 parent 3e13fd1 commit 19ca324
Show file tree
Hide file tree
Showing 37 changed files with 711 additions and 578 deletions.
39 changes: 8 additions & 31 deletions crates/analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::db::AnalyzerDb;
use crate::errors::{self, CannotMove};
use crate::namespace::events::EventDef;
use crate::namespace::items::EventId;
use crate::namespace::items::FunctionId;
use crate::namespace::types::{Array, Contract, FixedSize, Struct, Tuple, Type};
pub use fe_common::diagnostics::Label;
use fe_common::diagnostics::{Diagnostic, Severity};
Expand All @@ -18,10 +19,9 @@ use std::rc::Rc;
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Analysis<T> {
pub value: T,
pub diagnostics: Vec<Diagnostic>,
pub diagnostics: Rc<Vec<Diagnostic>>,
}

// TODO: rename to Context when the Context struct is gone
pub trait AnalyzerContext {
fn resolve_type(&self, name: &str) -> Option<Rc<Type>>;
fn add_diagnostic(&mut self, diag: Diagnostic);
Expand All @@ -44,11 +44,11 @@ pub trait AnalyzerContext {
)
}

fn not_yet_implemented(&mut self, feature: &dyn Display, span: Span) {
fn not_yet_implemented(&mut self, feature: &str, span: Span) {
self.error(
"feature not yet implemented".into(),
&format!("feature not yet implemented: {}", feature),
span,
&format!("{} is not yet implemented", feature),
"not yet implemented".into(),
)
}

Expand Down Expand Up @@ -89,7 +89,7 @@ impl Location {
}
}

#[derive(Default, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct FunctionBody {
pub expressions: BTreeMap<NodeId, ExpressionAttributes>,
pub emits: BTreeMap<NodeId, EventId>,
Expand All @@ -99,7 +99,7 @@ pub struct FunctionBody {
}

/// Contains contextual information relating to an expression AST node.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExpressionAttributes {
pub typ: Type,
pub location: Location,
Expand Down Expand Up @@ -146,34 +146,11 @@ impl ExpressionAttributes {
}

/// The type of a function call.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CallType {
BuiltinFunction { func: GlobalMethod },
TypeConstructor { typ: Type },
SelfAttribute { func_name: String },
ValueAttribute,
TypeAttribute { typ: Type, func_name: String },
}

// XXX: rename to FunctionType to differentiate between this and the analysis of the fn body
/// Contains contextual information relating to a function definition AST node.
#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct FunctionAttributes {
pub is_public: bool,
pub name: String,
pub params: Vec<(String, FixedSize)>,
pub return_type: FixedSize,
}

impl FunctionAttributes {
pub fn param_types(&self) -> Vec<FixedSize> {
self.params.iter().map(|(_, typ)| typ.to_owned()).collect()
}

pub fn param_names(&self) -> Vec<String> {
self.params
.iter()
.map(|(name, _)| name.to_owned())
.collect()
}
}
23 changes: 17 additions & 6 deletions crates/analyzer/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::context::{Analysis, FunctionAttributes, FunctionBody};
use crate::context::{Analysis, FunctionBody};
use crate::namespace::items::{
self, ContractId, EventId, FunctionId, ModuleId, StructId, TypeAliasId, TypeDefId,
};
use crate::namespace::{events, types};
use fe_common::diagnostics::Diagnostic;
use fe_parser::node::NodeId;
use indexmap::IndexMap;
use std::collections::BTreeMap;
Expand Down Expand Up @@ -76,14 +77,17 @@ pub trait AnalyzerDb {
#[salsa::invoke(queries::contracts::contract_type)]
fn contract_type(&self, contract: ContractId) -> Analysis<Rc<types::Contract>>;
#[salsa::invoke(queries::contracts::contract_functions)]
fn contract_functions(&self, contract: ContractId) -> Rc<IndexMap<String, FunctionId>>;
fn contract_functions(&self, contract: ContractId) -> Rc<Vec<FunctionId>>;
#[salsa::invoke(queries::contracts::contract_events)]
fn contract_events(&self, contract: ContractId) -> Rc<IndexMap<String, EventId>>;
fn contract_events(&self, contract: ContractId) -> Rc<Vec<EventId>>;
#[salsa::invoke(queries::contracts::contract_fields)]
fn contract_fields(&self, contract: ContractId) -> Rc<IndexMap<String, Rc<types::Type>>>;
fn contract_fields(
&self,
contract: ContractId,
) -> Analysis<Rc<IndexMap<String, Rc<types::Type>>>>;

#[salsa::invoke(queries::functions::function_type)]
fn function_type(&self, id: FunctionId) -> Analysis<Rc<FunctionAttributes>>;
#[salsa::invoke(queries::functions::function_signature)]
fn function_signature(&self, id: FunctionId) -> Analysis<Rc<types::FunctionSignature>>;
#[salsa::invoke(queries::functions::function_body)]
fn function_body(&self, id: FunctionId) -> Analysis<Rc<FunctionBody>>;

Expand All @@ -100,6 +104,13 @@ pub trait AnalyzerDb {
// fn contract_field_type(&self, contract: ContractId, name: String) -> Option<(Rc<types::Type>, usize)>;
}

#[salsa::database(AnalyzerDbStorage)]
#[derive(Default)]
pub struct TestDb {
storage: salsa::Storage<TestDb>,
}
impl salsa::Database for TestDb {}

// struct ItemLoc<N: ItemTreeNode>
// container: ModuleId
// id: ItemTreeId<N>
Expand Down
200 changes: 122 additions & 78 deletions crates/analyzer/src/db/queries/contracts.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,44 @@
use crate::context::AnalyzerContext;
use crate::db::{Analysis, AnalyzerDb};
use crate::errors;
use crate::namespace::items::{self, ContractId, EventId, FunctionId, ModuleId};
use crate::namespace::scopes::ItemScope;
use crate::namespace::types;
use fe_common::diagnostics::Diagnostic;
use crate::traversal::types::type_desc;
use fe_common::diagnostics::{Diagnostic, Label};
use fe_parser::ast;
use fe_parser::node::Node;
use indexmap::IndexMap;
use indexmap::{map::Entry, IndexMap};
use std::collections::HashSet;
use std::rc::Rc;

struct Ctx<'a> {
db: &'a dyn AnalyzerDb,
module: ModuleId,
diagnostics: Vec<Diagnostic>,
}
impl<'a> AnalyzerContext for Ctx<'a> {
fn resolve_type(&self, name: &str) -> Option<Rc<types::Type>> {
self.module.resolve_type(self.db, name)
}
fn add_diagnostic(&mut self, diag: Diagnostic) {
self.diagnostics.push(diag)
}
}

pub fn contract_functions(
db: &dyn AnalyzerDb,
contract: ContractId,
) -> Rc<IndexMap<String, FunctionId>> {
pub fn contract_functions(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<Vec<FunctionId>> {
let body = &contract.data(db).ast.kind.body;
Rc::new(
contract
.data(db)
.ast
.kind
.body
.iter()
body.iter()
.filter_map(|stmt| match stmt {
ast::ContractStmt::Event(_) => None,
ast::ContractStmt::Function(node) => Some((
node.kind.name.kind.clone(),
db.intern_function(Rc::new(items::Function {
ast::ContractStmt::Function(node) => {
Some(db.intern_function(Rc::new(items::Function {
ast: node.clone(),
contract,
})),
)),
})))
}
})
.collect(),
)
}

pub fn contract_events(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<IndexMap<String, EventId>> {
pub fn contract_events(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<Vec<EventId>> {
let body = &contract.data(db).ast.kind.body;
Rc::new(
contract
.data(db)
.ast
.kind
.body
.iter()
body.iter()
.filter_map(|stmt| match stmt {
ast::ContractStmt::Function(_) => None,
ast::ContractStmt::Event(node) => Some((
node.kind.name.kind.clone(),
db.intern_event(Rc::new(items::Event {
ast: node.clone(),
contract,
})),
)),
ast::ContractStmt::Event(node) => Some(db.intern_event(Rc::new(items::Event {
ast: node.clone(),
contract,
}))),
})
.collect(),
)
Expand All @@ -72,45 +47,114 @@ pub fn contract_events(db: &dyn AnalyzerDb, contract: ContractId) -> Rc<IndexMap
pub fn contract_fields(
db: &dyn AnalyzerDb,
contract: ContractId,
) -> Rc<IndexMap<String, Rc<types::Type>>> {
todo!()
) -> Analysis<Rc<IndexMap<String, Rc<types::Type>>>> {
let mut scope = ItemScope::new(db, contract.module(db));
let mut fields = IndexMap::new();

let contract_def = &contract.data(db).ast.kind;
for field in &contract_def.fields {
let name = field.kind.name.kind.clone();

if field.kind.is_pub {
scope.not_yet_implemented("contract `pub` fields", field.span);
}
if field.kind.is_const {
scope.not_yet_implemented("contract `const` fields", field.span);
}
if let Some(node) = &field.kind.value {
scope.not_yet_implemented("contract field initial value assignment", node.span);
}

match fields.entry(name) {
Entry::Occupied(entry) => {
scope.fancy_error(
&format!(
"duplicate field names in `contract {}`",
contract_def.name.kind,
),
vec![
Label::primary(
contract_def.field_span(entry.key()).unwrap(),
format!("`{}` first defined here", entry.key()),
),
Label::secondary(field.span, format!("`{}` redefined here", entry.key())),
],
vec![],
);
}
Entry::Vacant(entry) => {
entry.insert(Rc::new(type_desc(&mut scope, &field.kind.typ)));
}
}
}

Analysis {
value: Rc::new(fields),
diagnostics: Rc::new(scope.diagnostics),
}
}

pub fn contract_type(db: &dyn AnalyzerDb, contract: ContractId) -> Analysis<Rc<types::Contract>> {
let mut ctx = Ctx {
db,
module: contract.module(db),
diagnostics: vec![],
let mut contract_typ = types::Contract {
name: contract.name(db),
functions: IndexMap::new(),
};
let mut diagnostics = vec![];

for func in contract.functions(db).iter() {
let def = &func.data(db).ast;
let name = def.kind.name.kind.clone();

// context.fancy_error(
// "a function with the same name already exists",
// // TODO: figure out how to include the previously defined function
// vec![Label::primary(
// def.span,
// format!("Conflicting definition of contract `{}`", name),
// )],
// vec![format!(
// "Note: Give one of the `{}` functions a different name",
// name
// )],
// )
match contract_typ.functions.entry(name) {
Entry::Occupied(entry) => {
diagnostics.push(errors::fancy_error(
format!(
"duplicate function names in `contract {}`",
contract_typ.name,
),
vec![
Label::primary(
entry.get().id.data(db).ast.span,
format!("`{}` first defined here", entry.key()),
),
Label::secondary(def.span, format!("`{}` redefined here", entry.key())),
],
vec![],
));
}
Entry::Vacant(entry) => {
let mut signature = func.signature(db);
let mut is_public = def.kind.is_pub;

// for func in contract.functions(db) {
// let func.data(db).ast
// }
if entry.key() == "__init__" {
// `__init__` must be `pub`.
if !is_public {
diagnostics.push(errors::fancy_error(
"`__init__` function is not public",
vec![Label::primary(
def.span,
"`__init__` function must be public",
)],
vec![
"Hint: Add the `pub` modifier.".to_string(),
"Example: `pub def __init__():`".to_string(),
],
));
is_public = true;
}
}

// let functions = body.iter().filter_map(|stmt| match stmt {
// fe::ContractStmt::EventDef(_) => None,
// fe::ContractStmt::FuncDef(func) =>
// })
entry.insert(Rc::new(types::ContractFunction {
is_public,
id: *func,
signature,
}));
}
}
}

// Analysis {
// value: Rc::new(types::Contract {
// name: name.clone(),
// functions,
// }),
// diagnostics,
// }
todo!()
Analysis {
value: Rc::new(contract_typ),
diagnostics: Rc::new(diagnostics),
}
}
Loading

0 comments on commit 19ca324

Please sign in to comment.