Skip to content

Commit

Permalink
Merge 96bc925 into d5c43d5
Browse files Browse the repository at this point in the history
  • Loading branch information
sbillig authored Mar 29, 2022
2 parents d5c43d5 + 96bc925 commit 6d00d3c
Show file tree
Hide file tree
Showing 160 changed files with 1,745 additions and 928 deletions.
9 changes: 8 additions & 1 deletion crates/analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ pub enum NamedThing {
Variable {
name: String,
typ: Result<FixedSize, TypeError>,
is_const: bool,
kind: NameBindingKind,
span: Span,
},
}
Expand Down Expand Up @@ -255,6 +255,13 @@ impl NamedThing {
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NameBindingKind {
Const,
Let,
LetMut,
}

/// This should only be created by [`AnalyzerContext`].
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DiagnosticVoucher(PhantomData<()>);
Expand Down
29 changes: 15 additions & 14 deletions crates/analyzer/src/db/queries/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ pub fn function_signature(
.iter()
.enumerate()
.filter_map(|(index, arg)| match &arg.kind {
ast::FunctionArg::Self_ => {
ast::FunctionArg::Self_ { mut_ } => {
if fn_parent.is_none() {
scope.error(
"`self` can only be used in contract or struct functions",
arg.span,
"not allowed in functions defined outside of a contract or struct",
);
} else {
self_decl = Some(SelfDecl::Mutable);
self_decl = Some(if mut_.is_some() { SelfDecl::Mutable } else { SelfDecl::Immutable });
if index != 0 {
scope.error(
"`self` is not the first parameter",
Expand All @@ -67,12 +67,12 @@ pub fn function_signature(
}
None
}
ast::FunctionArg::Regular(reg) => {
let typ = type_desc(&mut scope, &reg.typ).and_then(|typ| match typ.try_into() {
ast::FunctionArg::Regular { mut_, label, name, typ: typdesc } => {
let typ = type_desc(&mut scope, &typdesc).and_then(|typ| match typ.try_into() {
Ok(typ) => Ok(typ),
Err(_) => Err(TypeError::new(scope.error(
"function parameter types must have fixed size",
reg.typ.span,
typdesc.span,
"`Map` type can't be used as a function parameter",
))),
});
Expand Down Expand Up @@ -109,7 +109,7 @@ pub fn function_signature(
}
}

if let Some(label) = &reg.label {
if let Some(label) = &label {
if_chain! {
if label.kind != "_";
if let Some(dup_idx) = labels.get(&label.kind);
Expand All @@ -128,30 +128,31 @@ pub fn function_signature(
}
}

if let Ok(Some(named_item)) = scope.resolve_name(&reg.name.kind, reg.name.span) {
if let Ok(Some(named_item)) = scope.resolve_name(&name.kind, name.span) {
scope.name_conflict_error(
"function parameter",
&reg.name.kind,
&name.kind,
&named_item,
named_item.name_span(db),
reg.name.span,
name.span,
);
None
} else if let Some(dup_idx) = names.get(&reg.name.kind) {
} else if let Some(dup_idx) = names.get(&name.kind) {
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
scope.duplicate_name_error(
&format!("duplicate parameter names in function `{}`", def.name.kind),
&reg.name.kind,
&name.kind,
dup_arg.span,
arg.span,
);
None
} else {
names.insert(&reg.name.kind, index);
names.insert(&name.kind, index);
Some(types::FunctionParam::new(
reg.label.as_ref().map(|s| s.kind.as_str()),
&reg.name.kind,
label.as_ref().map(|s| s.kind.as_str()),
&name.kind,
typ,
mut_.is_some(),
))
}
}
Expand Down
30 changes: 9 additions & 21 deletions crates/analyzer/src/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ use crate::context;

use crate::context::{Analysis, Constant};
use crate::errors::{self, IncompleteItem, TypeError};
use crate::namespace::types::FixedSize;
use crate::namespace::types::{self, GenericType};
use crate::namespace::types::{self, FixedSize, GenericType, SelfDecl};
use crate::traversal::pragma::check_pragma_version;
use crate::AnalyzerDb;
use crate::{builtins, errors::ConstEvalError};
Expand Down Expand Up @@ -983,7 +982,7 @@ impl ContractId {
) -> Result<Option<Item>, IncompleteItem> {
if let Some(item) = self
.function(db, name)
.filter(|f| !f.takes_self(db))
.filter(|f| f.self_decl(db).is_none())
.map(Item::Function)
.or_else(|| self.event(db, name).map(Item::Event))
{
Expand Down Expand Up @@ -1021,11 +1020,6 @@ impl ContractId {
db.contract_public_function_map(*self)
}

/// Get a function that takes self by its name.
pub fn self_function(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionId> {
self.function(db, name).filter(|f| f.takes_self(db))
}

/// Lookup an event by name.
pub fn event(&self, db: &dyn AnalyzerDb, name: &str) -> Option<EventId> {
self.events(db).get(name).copied()
Expand Down Expand Up @@ -1151,17 +1145,14 @@ impl FunctionId {
self.data(db).module
}

pub fn takes_self(&self, db: &dyn AnalyzerDb) -> bool {
self.signature(db).self_decl.is_some()
pub fn self_decl(&self, db: &dyn AnalyzerDb) -> Option<SelfDecl> {
self.signature(db).self_decl
}
pub fn self_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
if self.takes_self(db) {
self.data(db)
.ast
.kind
.args
.iter()
.find_map(|arg| matches!(arg.kind, ast::FunctionArg::Self_).then(|| arg.span))
if self.self_decl(db).is_some() {
self.data(db).ast.kind.args.iter().find_map(|arg| {
matches!(arg.kind, ast::FunctionArg::Self_ { .. }).then(|| arg.span)
})
} else {
None
}
Expand Down Expand Up @@ -1212,7 +1203,7 @@ impl Class {
}
pub fn self_function(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionId> {
let fun = self.function(db, name)?;
fun.takes_self(db).then(|| fun)
fun.self_decl(db).map(|_| fun)
}

pub fn name(&self, db: &dyn AnalyzerDb) -> SmolStr {
Expand Down Expand Up @@ -1319,9 +1310,6 @@ impl StructId {
pub fn function(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionId> {
self.functions(db).get(name).copied()
}
pub fn self_function(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionId> {
self.function(db, name).filter(|f| f.takes_self(db))
}
pub fn parent(&self, db: &dyn AnalyzerDb) -> Item {
Item::Module(self.data(db).module)
}
Expand Down
20 changes: 12 additions & 8 deletions crates/analyzer/src/namespace/scopes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#![allow(unstable_name_collisions)] // expect_none, which ain't gonna be stabilized

use crate::context::{
AnalyzerContext, CallType, Constant, ExpressionAttributes, FunctionBody, NamedThing,
AnalyzerContext, CallType, Constant, ExpressionAttributes, FunctionBody, NameBindingKind,
NamedThing,
};
use crate::errors::{AlreadyDefined, IncompleteItem, TypeError};
use crate::namespace::items::{Class, EventId, FunctionId, ModuleId};
Expand Down Expand Up @@ -333,7 +334,11 @@ impl<'a> AnalyzerContext for FunctionScope<'a> {
NamedThing::Variable {
name: name.to_string(),
typ: param.typ.clone(),
is_const: false,
kind: if param.is_mut {
NameBindingKind::LetMut
} else {
NameBindingKind::Let
},
span,
}
})
Expand Down Expand Up @@ -389,8 +394,7 @@ impl<'a> AnalyzerContext for FunctionScope<'a> {
pub struct BlockScope<'a, 'b> {
pub root: &'a FunctionScope<'b>,
pub parent: Option<&'a BlockScope<'a, 'b>>,
/// Maps Name -> (Type, is_const, span)
pub variable_defs: BTreeMap<String, (FixedSize, bool, Span)>,
pub variable_defs: BTreeMap<String, (FixedSize, NameBindingKind, Span)>,
pub constant_defs: RefCell<BTreeMap<String, Constant>>,
pub typ: BlockScopeType,
}
Expand All @@ -412,10 +416,10 @@ impl AnalyzerContext for BlockScope<'_, '_> {
if let Some(var) =
self.variable_defs
.get(name)
.map(|(typ, is_const, span)| NamedThing::Variable {
.map(|(typ, kind, span)| NamedThing::Variable {
name: name.to_string(),
typ: Ok(typ.clone()),
is_const: *is_const,
kind: *kind,
span: *span,
})
{
Expand Down Expand Up @@ -534,7 +538,7 @@ impl<'a, 'b> BlockScope<'a, 'b> {
&mut self,
name: &str,
typ: FixedSize,
is_const: bool,
binding_kind: NameBindingKind,
span: Span,
) -> Result<(), AlreadyDefined> {
match self.resolve_name(name, span) {
Expand Down Expand Up @@ -577,7 +581,7 @@ impl<'a, 'b> BlockScope<'a, 'b> {
}
_ => {
self.variable_defs
.insert(name.to_string(), (typ, is_const, span));
.insert(name.to_string(), (typ, binding_kind, span));
Ok(())
}
}
Expand Down
10 changes: 9 additions & 1 deletion crates/analyzer/src/namespace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ pub struct FunctionSignature {
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub enum SelfDecl {
Mutable,
Immutable,
}

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
Expand All @@ -174,13 +175,20 @@ pub enum CtxDecl {

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct FunctionParam {
pub is_mut: bool,
label: Option<SmolStr>,
pub name: SmolStr,
pub typ: Result<FixedSize, TypeError>,
}
impl FunctionParam {
pub fn new(label: Option<&str>, name: &str, typ: Result<FixedSize, TypeError>) -> Self {
pub fn new(
label: Option<&str>,
name: &str,
typ: Result<FixedSize, TypeError>,
is_mut: bool,
) -> Self {
Self {
is_mut,
label: label.map(SmolStr::new),
name: name.into(),
typ,
Expand Down
16 changes: 11 additions & 5 deletions crates/analyzer/src/traversal/assignments.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::context::{AnalyzerContext, Location, NamedThing};
use crate::context::{AnalyzerContext, Location, NameBindingKind, NamedThing};
use crate::errors::FatalError;
use crate::namespace::scopes::BlockScope;
use crate::operations;
Expand Down Expand Up @@ -77,13 +77,19 @@ fn check_assign_target(scope: &mut BlockScope, expr: &Node<fe::Expr>) -> Result<
Name(name) => match scope.resolve_name(name, expr.span)? {
Some(NamedThing::SelfValue { .. }) => Ok(()),
Some(NamedThing::Item(_)) | None => Err(invalid_assign_target(scope, expr)),
Some(NamedThing::Variable { is_const, .. }) => {
if is_const {
Some(NamedThing::Variable { kind, .. }) => match kind {
NameBindingKind::LetMut => Ok(()),

NameBindingKind::Let => {
scope.fancy_error(&format!("`{}` is not mutable", name), // XXX better error
vec![Label::primary(expr.span, "")],
vec![]);
Ok(())
}
NameBindingKind::Const => {
Err(FatalError::new(scope.fancy_error("cannot assign to constant variable",
vec![Label::primary(expr.span, "")],
vec!["The left side of an assignment can be a variable name, attribute, subscript, or tuple.".into()])))
} else {
Ok(())
}
}
},
Expand Down
29 changes: 23 additions & 6 deletions crates/analyzer/src/traversal/declarations.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::context::AnalyzerContext;
use crate::context::{AnalyzerContext, NameBindingKind};
use crate::errors::FatalError;
use crate::namespace::scopes::BlockScope;
use crate::namespace::types::FixedSize;
Expand All @@ -9,7 +9,13 @@ use fe_parser::node::Node;

/// Gather context information for var declarations and check for type errors.
pub fn var_decl(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(), FatalError> {
if let fe::FuncStmt::VarDecl { target, typ, value } = &stmt.kind {
if let fe::FuncStmt::VarDecl {
mut_,
target,
typ,
value,
} = &stmt.kind
{
let declared_type = match FixedSize::try_from(types::type_desc(scope, typ)?) {
Ok(typ) => typ,
Err(_) => {
Expand Down Expand Up @@ -37,7 +43,12 @@ pub fn var_decl(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(),
}

scope.root.add_declaration(typ, declared_type.clone());
add_var(scope, target, declared_type)?;
let mutability = match mut_ {
Some(_) => NameBindingKind::LetMut,
None => NameBindingKind::Let,
};

add_var(scope, target, declared_type, mutability)?;
return Ok(());
}

Expand Down Expand Up @@ -79,7 +90,12 @@ pub fn const_decl(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(
.root
.map_variable_type(name, declared_type.clone().into());
// this logs a message on err, so it's safe to ignore here.
let _ = scope.add_var(name.kind.as_str(), declared_type, true, name.span);
let _ = scope.add_var(
name.kind.as_str(),
declared_type,
NameBindingKind::Const,
name.span,
);
scope.add_constant(name, value, const_value);
return Ok(());
}
Expand All @@ -92,12 +108,13 @@ fn add_var(
scope: &mut BlockScope,
target: &Node<fe::VarDeclTarget>,
typ: FixedSize,
mutability: NameBindingKind,
) -> Result<(), FatalError> {
match &target.kind {
fe::VarDeclTarget::Name(name) => {
scope.root.map_variable_type(target, typ.clone().into());
// this logs a message on err, so it's safe to ignore here.
let _ = scope.add_var(name, typ, false, target.span);
let _ = scope.add_var(name, typ, mutability, target.span);
Ok(())
}
fe::VarDeclTarget::Tuple(items) => {
Expand All @@ -119,7 +136,7 @@ fn add_var(
)));
}
for (item, item_ty) in items.iter().zip(items_ty.into_iter()) {
add_var(scope, item, item_ty)?;
add_var(scope, item, item_ty, mutability)?;
}
Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions crates/analyzer/src/traversal/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1362,7 +1362,7 @@ fn expr_call_method(
if let Some(method) = class.function(context.db(), &field.kind) {
let is_self = is_self_value(target);

if is_self && !method.takes_self(context.db()) {
if is_self && method.self_decl(context.db()).is_none() {
context.fancy_error(
&format!("`{}` must be called without `self`", &field.kind),
vec![Label::primary(field.span, "function does not take self")],
Expand Down Expand Up @@ -1716,7 +1716,7 @@ fn expr_call_type_attribute(
}

if let Some(function) = class.function(context.db(), &field.kind) {
if function.takes_self(context.db()) {
if function.self_decl(context.db()).is_some() {
return Err(FatalError::new(context.fancy_error(
&format!(
"`{}` function `{}` must be called on an instance of `{}`",
Expand Down
Loading

0 comments on commit 6d00d3c

Please sign in to comment.