Skip to content

Commit

Permalink
Basic mut enforcement, remove .clone() function
Browse files Browse the repository at this point in the history
  • Loading branch information
sbillig committed Aug 31, 2022
1 parent 449e31a commit 7cc8015
Show file tree
Hide file tree
Showing 234 changed files with 3,366 additions and 3,332 deletions.
1 change: 0 additions & 1 deletion crates/analyzer/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use strum::{AsRefStr, EnumIter, EnumString};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, EnumString, AsRefStr)]
#[strum(serialize_all = "snake_case")]
pub enum ValueMethod {
Clone,
ToMem,
AbiEncode,
}
Expand Down
17 changes: 12 additions & 5 deletions crates/analyzer/src/db/queries/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ pub fn function_signature(
.iter()
.enumerate()
.filter_map(|(index, arg)| match &arg.kind {
ast::FunctionArg::Self_ { .. }=> {
ast::FunctionArg::Self_ { mut_ }=> {
if matches!(fn_parent, Item::Module(_)) {
scope.error(
"`self` can only be used in contract, struct, trait or impl functions",
arg.span,
"not allowed in functions defined directly in a module",
);
} else {
self_decl = Some(SelfDecl::Mutable);
self_decl = Some(SelfDecl { span: arg.span, mut_: mut_.clone() });
if index != 0 {
scope.error(
"`self` is not the first parameter",
Expand All @@ -104,9 +104,15 @@ pub fn function_signature(
}
None
}
ast::FunctionArg::Regular { mut_: _, label, name, typ: typedesc } => {
ast::FunctionArg::Regular { mut_, label, name, typ: typedesc } => {
let typ = resolve_function_param_type(db, function, &mut scope, &typedesc).and_then(|typ| match typ {
typ if typ.has_fixed_size(db) => Ok(typ),
typ if typ.has_fixed_size(db) => {
if mut_.is_some() {
Ok(Type::Mut(typ).id(db))
} else {
Ok(typ)
}
}
_ => Err(TypeError::new(scope.error(
"function parameter types must have fixed size",
typedesc.span,
Expand Down Expand Up @@ -177,6 +183,7 @@ pub fn function_signature(
None
} else {
names.insert(&name.kind, index);

Some(types::FunctionParam::new(
label.as_ref().map(|s| s.kind.as_str()),
&name.kind,
Expand Down Expand Up @@ -229,7 +236,7 @@ pub fn function_signature(
}
}

pub fn resolve_function_param_type(
fn resolve_function_param_type(
db: &dyn AnalyzerDb,
function: FunctionSigId,
context: &mut dyn AnalyzerContext,
Expand Down
37 changes: 32 additions & 5 deletions crates/analyzer/src/namespace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ impl TypeId {
pub fn deref(self, db: &dyn AnalyzerDb) -> TypeId {
match self.typ(db) {
Type::SPtr(inner) => inner,
Type::Mut(inner) => inner.deref(db),
_ => self,
}
}
Expand Down Expand Up @@ -115,7 +116,18 @@ impl TypeId {
matches!(self.typ(db), Type::Struct(_))
}
pub fn is_sptr(&self, db: &dyn AnalyzerDb) -> bool {
matches!(self.typ(db), Type::SPtr(_))
match self.typ(db) {
Type::SPtr(_) => true,
Type::Mut(inner) => inner.is_sptr(db),
_ => false,
}
}
pub fn is_generic(&self, db: &dyn AnalyzerDb) -> bool {
matches!(self.deref(db).typ(db), Type::Generic(_))
}

pub fn is_mut(&self, db: &dyn AnalyzerDb) -> bool {
matches!(self.typ(db), Type::Mut(_))
}

pub fn name(&self, db: &dyn AnalyzerDb) -> SmolStr {
Expand Down Expand Up @@ -245,9 +257,15 @@ pub struct FunctionSignature {
pub return_type: Result<TypeId, TypeError>,
}

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub enum SelfDecl {
Mutable,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct SelfDecl {
pub span: Span,
pub mut_: Option<Span>,
}
impl SelfDecl {
pub fn is_mut(&self) -> bool {
self.mut_.is_some()
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -669,9 +687,18 @@ impl DisplayWithDb for FunctionSignature {
return_type,
} = self;

write!(f, "self: {:?}, ", self_decl)?;
write!(f, "params: [")?;
let mut delim = "";
if let Some(s) = self_decl {
write!(
f,
"{}{}",
if s.mut_.is_some() { "mut " } else { "" },
"self"
)?;
delim = ", ";
}

for p in params {
write!(
f,
Expand Down
2 changes: 2 additions & 0 deletions crates/analyzer/src/operations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::context::AnalyzerContext;
use crate::errors::{BinaryOperationError, IndexingError};
use crate::namespace::types::{Array, Integer, Map, Type, TypeDowncast, TypeId};

use crate::traversal::types::{deref_type, try_coerce_type};
use fe_parser::{ast as fe, node::Node};

Expand Down Expand Up @@ -53,6 +54,7 @@ fn index_map(
index_expr: &Node<fe::Expr>,
) -> Result<TypeId, IndexingError> {
let Map { key, value } = map;

if try_coerce_type(context, Some(index_expr), index, *key).is_err() {
return Err(IndexingError::WrongIndexType);
}
Expand Down
108 changes: 70 additions & 38 deletions crates/analyzer/src/traversal/assignments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use crate::context::{AnalyzerContext, NamedThing};
use crate::display::Displayable;
use crate::errors::{self, FatalError, TypeCoercionError};
use crate::namespace::scopes::BlockScope;
use crate::namespace::types::{Type, TypeId};
use crate::operations;
use crate::traversal::utils::add_bin_operations_errors;
use crate::traversal::{expressions, types::try_coerce_type};
use fe_common::diagnostics::Label;
use fe_parser::ast as fe;
use fe_parser::node::Node;
use fe_parser::node::{Node, Span};
use smol_str::SmolStr;

/// Gather context information for assignments and check for type errors.
///
Expand All @@ -17,18 +19,13 @@ pub fn assign(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(), F
fe::FuncStmt::Assign { target, value } => (target, value),
_ => unreachable!(),
};
let target_attributes = expressions::expr(scope, target, None)?;

let expected_type = target_attributes.typ.deref(scope.db());
let value_attributes = expressions::expr(scope, value, Some(expected_type))?;
check_assign_target(scope, target)?;
let target_type = assign_target_type(scope, target)?;
let expected_type = target_type.deref(scope.db());
let value_attributes = expressions::expr(scope, value, Some(expected_type))?;

match try_coerce_type(
scope,
Some(value),
value_attributes.typ,
target_attributes.typ,
) {
match try_coerce_type(scope, Some(value), value_attributes.typ, target_type) {
Err(TypeCoercionError::RequiresToMem) => {
scope.add_diagnostic(errors::to_mem_error(value.span));
}
Expand All @@ -40,7 +37,7 @@ pub fn assign(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(), F
target.span,
format!(
"this variable has type `{}`",
target_attributes.typ.display(scope.db())
target_type.display(scope.db())
),
),
Label::secondary(
Expand All @@ -57,14 +54,52 @@ pub fn assign(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(), F
Err(TypeCoercionError::SelfContractType) => {
scope.add_diagnostic(errors::self_contract_type_error(
value.span,
&target_attributes.typ.display(scope.db()),
&target_type.display(scope.db()),
));
}
Ok(_) => {}
}
Ok(())
}

fn assign_target_type(
scope: &mut BlockScope,
target: &Node<fe::Expr>,
) -> Result<TypeId, FatalError> {
let attr = expressions::expr(scope, target, None)?;
match attr.typ.typ(scope.db()) {
Type::Mut(inner) => Ok(inner),
_ => {
let mut labels = vec![Label::primary(target.span, "not mutable")];
if let Some((name, span)) = name_def_span(scope, target) {
labels.push(Label::secondary(
span,
&format!("consider changing this to be mutable: `mut {}`", name),
));
}
scope.fancy_error(
&format!("cannot modify `{}`, as it is not mutable", &target.kind),
labels,
vec![],
);
Ok(attr.typ)
}
}
}

fn name_def_span(scope: &BlockScope, expr: &Node<fe::Expr>) -> Option<(SmolStr, Span)> {
match &expr.kind {
fe::Expr::Attribute { value, .. } | fe::Expr::Subscript { value, .. } => {
name_def_span(scope, value)
}
fe::Expr::Name(name) => {
let thing = scope.resolve_name(name, expr.span).ok()??;
thing.name_span(scope.db()).map(|span| (name.clone(), span))
}
_ => None,
}
}

fn check_assign_target(scope: &mut BlockScope, expr: &Node<fe::Expr>) -> Result<(), FatalError> {
use fe::Expr::*;

Expand All @@ -82,7 +117,7 @@ fn check_assign_target(scope: &mut BlockScope, expr: &Node<fe::Expr>) -> Result<
Some(NamedThing::Item(_)) | None => Err(invalid_assign_target(scope, expr)),
Some(NamedThing::Variable { is_const, .. }) => {
if is_const {
Err(FatalError::new(scope.fancy_error("cannot assign to constant variable",
Err(FatalError::new(scope.fancy_error("cannot assign to a constant value",
vec![Label::primary(expr.span, "")],
vec!["The left side of an assignment can be a variable name, attribute, subscript, or tuple.".into()])))
} else {
Expand All @@ -96,38 +131,35 @@ fn check_assign_target(scope: &mut BlockScope, expr: &Node<fe::Expr>) -> Result<
}

fn invalid_assign_target(scope: &mut BlockScope, expr: &Node<fe::Expr>) -> FatalError {
FatalError::new(scope.fancy_error("invalid assignment target",
vec![Label::primary(expr.span, "")],
vec!["The left side of an assignment can be a variable name, attribute, subscript, or tuple.".into()]))
FatalError::new(scope.fancy_error(
"invalid assignment target",
vec![Label::primary(expr.span, "")],
vec!["The left side of an assignment can be a variable name, attribute, subscript, or tuple.".into()]
))
}

/// Gather context information for assignments and check for type errors.
pub fn aug_assign(scope: &mut BlockScope, stmt: &Node<fe::FuncStmt>) -> Result<(), FatalError> {
if let fe::FuncStmt::AugAssign { target, op, value } = &stmt.kind {
check_assign_target(scope, target)?;
let target_attr = expressions::expr(scope, target, None)?;
let value_attr = expressions::expr(scope, value, Some(target_attr.typ))?;
let (target, op, value) = match &stmt.kind {
fe::FuncStmt::AugAssign { target, op, value } => (target, op, value),
_ => unreachable!(),
};

if let Err(err) = operations::bin(
check_assign_target(scope, target)?;
let target_ty = assign_target_type(scope, target)?; // XXX check this
let value_attr = expressions::expr(scope, value, Some(target_ty))?;

if let Err(err) = operations::bin(scope, target_ty, target, op.kind, value_attr.typ, value) {
add_bin_operations_errors(
scope,
target_attr.typ,
target,
op.kind,
&op.kind,
target.span,
target_ty,
value.span,
value_attr.typ,
value,
) {
add_bin_operations_errors(
scope,
&op.kind,
target.span,
target_attr.typ,
value.span,
value_attr.typ,
err,
);
}
return Ok(());
err,
);
}

unreachable!()
Ok(())
}
3 changes: 3 additions & 0 deletions crates/analyzer/src/traversal/call_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ pub fn validate_named_args(
Ok(_) => {}
}
}
if param_type.is_mut(context.db()) && !arg_type.is_mut(context.db()) {
context.error("XXX need mut", arg.span, "make this `mut`");
}
}
Ok(())
}
Loading

0 comments on commit 7cc8015

Please sign in to comment.