Skip to content

Commit

Permalink
Merge pull request #649 from Y-Nak/constant-evaluation
Browse files Browse the repository at this point in the history
Constant evaluation
  • Loading branch information
g-r-a-n-t authored Feb 4, 2022
2 parents 3ec1711 + ab61912 commit 897f159
Show file tree
Hide file tree
Showing 45 changed files with 2,684 additions and 432 deletions.
155 changes: 153 additions & 2 deletions crates/analyzer/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
use crate::builtins::{ContractTypeMethod, GlobalFunction, Intrinsic, ValueMethod};
use crate::errors::{self, CannotMove, TypeError};
use crate::namespace::items::{Class, ContractId, DiagnosticSink, EventId, FunctionId, Item};
use crate::namespace::types::{FixedSize, SelfDecl, Type};
use crate::AnalyzerDb;
use crate::{
builtins::{ContractTypeMethod, GlobalFunction, Intrinsic, ValueMethod},
namespace::scopes::BlockScopeType,
};
use fe_common::diagnostics::Diagnostic;
pub use fe_common::diagnostics::Label;
use fe_common::Span;
use fe_parser::ast;
use fe_parser::node::NodeId;
use fe_parser::node::{Node, NodeId};

use indexmap::{IndexMap, IndexSet};
use num_bigint::BigInt;
use smol_str::SmolStr;
use std::collections::HashMap;
use std::fmt::{self, Debug, Display};
Expand Down Expand Up @@ -38,6 +42,94 @@ pub trait AnalyzerContext {
self.register_diag(errors::error(message, label_span, label))
}

/// Attribute contextual information to an expression node.
///
/// # Panics
///
/// Panics if an entry already exists for the node id.
fn add_expression(&self, node: &Node<ast::Expr>, attributes: ExpressionAttributes);

/// Update the expression attributes.
///
/// # Panics
///
/// Panics if an entry does not already exist for the node id.
fn update_expression(&self, node: &Node<ast::Expr>, attributes: ExpressionAttributes);

/// Returns a type of an expression.
///
/// # Panics
///
/// Panics if type analysis is not performed for an `expr`.
fn expr_typ(&self, expr: &Node<ast::Expr>) -> Type;

/// Add evaluated constant value in a constant declaration to the context.
fn add_constant(&self, name: &Node<ast::SmolStr>, expr: &Node<ast::Expr>, value: Constant);

/// Returns constant value from variable name.
fn constant_value_by_name(&self, name: &ast::SmolStr) -> Option<Constant>;

/// Returns an item enclosing current context.
///
/// # Example
///
/// ```ignore
/// contract Foo:
/// fn foo():
/// if ...:
/// ...
/// else:
/// ...
/// ```
/// If the context is in `then` block, then this function returns `Item::Function(..)`.
fn parent(&self) -> Item;

/// Returns a function id that encloses a context.
///
/// # Panics
///
/// Panics if a context is not in a function. Use [`Self::is_in_function`] to determine whether a context is in a function.
fn parent_function(&self) -> FunctionId;

/// Returns a non-function item that encloses a context.
///
/// # Example
///
/// ```ignore
/// contract Foo:
/// fn foo():
/// if ...:
/// ...
/// else:
/// ...
/// ```
/// If the context is in `then` block, then this function returns `Item::Type(TypeDef::Contract(..))`.
fn root_item(&self) -> Item {
let mut item = self.parent();
while let Item::Function(func_id) = item {
item = func_id.parent(self.db());
}
item
}

/// # Panics
///
/// Panics if a context is not in a function. Use [`Self::is_in_function`] to determine whether a context is in a function.
fn add_call(&self, node: &Node<ast::Expr>, call_type: CallType);

/// Store string literal to the current context.
///
/// # Panics
///
/// Panics if a context is not in a function. Use [`Self::is_in_function`] to determine whether a context is in a function.
fn add_string(&self, str_lit: SmolStr);

/// Returns `true` if the context is in function scope.
fn is_in_function(&self) -> bool;

/// Returns `true` if the scope or any of its parents is of the given type.
fn inherits_type(&self, typ: BlockScopeType) -> bool;

fn type_error(
&mut self,
message: &str,
Expand Down Expand Up @@ -112,6 +204,7 @@ pub enum NamedThing {
Variable {
name: String,
typ: Result<FixedSize, TypeError>,
is_const: bool,
span: Span,
},
}
Expand Down Expand Up @@ -154,12 +247,59 @@ impl AnalyzerContext for TempContext {
fn db(&self) -> &dyn AnalyzerDb {
panic!("TempContext has no analyzer db")
}

fn resolve_name(&self, _name: &str) -> Option<NamedThing> {
panic!("TempContext can't resolve names")
}

fn add_expression(&self, _node: &Node<ast::Expr>, _attributes: ExpressionAttributes) {
panic!("TempContext can't store expression")
}

fn update_expression(&self, _node: &Node<ast::Expr>, _attributes: ExpressionAttributes) {
panic!("TempContext can't update expression");
}

fn expr_typ(&self, _expr: &Node<ast::Expr>) -> Type {
panic!("TempContext can't return expression type")
}

fn add_constant(&self, _name: &Node<ast::SmolStr>, _expr: &Node<ast::Expr>, _value: Constant) {
panic!("TempContext can't store constant")
}

fn constant_value_by_name(&self, _name: &ast::SmolStr) -> Option<Constant> {
None
}

fn parent(&self) -> Item {
panic!("TempContext has no root item")
}

fn parent_function(&self) -> FunctionId {
panic!("TempContext has no parent function")
}

fn add_call(&self, _node: &Node<ast::Expr>, _call_type: CallType) {
panic!("TempContext can't add call");
}

fn add_string(&self, _str_lit: SmolStr) {
panic!("TempContext can't store string literal")
}

fn is_in_function(&self) -> bool {
false
}

fn inherits_type(&self, _typ: BlockScopeType) -> bool {
false
}

fn add_diagnostic(&mut self, diag: Diagnostic) {
self.diagnostics.push(diag)
}

fn resolve_path(&mut self, _path: &ast::Path) -> Option<NamedThing> {
panic!("TempContext can't resolve paths")
}
Expand Down Expand Up @@ -210,6 +350,8 @@ pub struct ExpressionAttributes {
pub typ: Type,
pub location: Location,
pub move_location: Option<Location>,
// Evaluated constant value of const local definition.
pub const_value: Option<Constant>,
}

impl ExpressionAttributes {
Expand All @@ -218,6 +360,7 @@ impl ExpressionAttributes {
typ,
location,
move_location: None,
const_value: None,
}
}

Expand Down Expand Up @@ -338,3 +481,11 @@ impl fmt::Display for CallType {
write!(f, "{:?}", self)
}
}

/// Represents constant value.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Constant {
Int(BigInt),
Bool(bool),
Str(SmolStr),
}
13 changes: 11 additions & 2 deletions crates/analyzer/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::context::{Analysis, FunctionBody};
use crate::errors::TypeError;
use crate::context::{Analysis, Constant, FunctionBody};
use crate::errors::{ConstEvalError, TypeError};
use crate::namespace::items::{
self, ContractFieldId, ContractId, DepGraphWrapper, EventId, FunctionId, GlobalId, IngotId,
Item, ModuleConstantId, ModuleId, StructFieldId, StructId, TypeAliasId,
Expand Down Expand Up @@ -73,6 +73,8 @@ pub trait AnalyzerDb {
fn module_contracts(&self, module: ModuleId) -> Rc<Vec<ContractId>>;
#[salsa::invoke(queries::module::module_structs)]
fn module_structs(&self, module: ModuleId) -> Rc<Vec<StructId>>;
#[salsa::invoke(queries::module::module_constants)]
fn module_constants(&self, module: ModuleId) -> Rc<Vec<ModuleConstantId>>;
#[salsa::invoke(queries::module::module_used_item_map)]
fn module_used_item_map(
&self,
Expand All @@ -84,11 +86,18 @@ pub trait AnalyzerDb {
fn module_sub_modules(&self, module: ModuleId) -> Rc<IndexMap<SmolStr, ModuleId>>;

// Module Constant
#[salsa::cycle(queries::module::module_constant_type_cycle)]
#[salsa::invoke(queries::module::module_constant_type)]
fn module_constant_type(
&self,
id: ModuleConstantId,
) -> Analysis<Result<types::Type, TypeError>>;
#[salsa::cycle(queries::module::module_constant_value_cycle)]
#[salsa::invoke(queries::module::module_constant_value)]
fn module_constant_value(
&self,
id: ModuleConstantId,
) -> Analysis<Result<Constant, ConstEvalError>>;

// Contract
#[salsa::invoke(queries::contracts::contract_all_functions)]
Expand Down
Loading

0 comments on commit 897f159

Please sign in to comment.