Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

builtin contract creation on ctx #696

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/analyzer/src/builtins.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::namespace::items::ContractId;
use crate::namespace::types::Base;
use strum::{AsRefStr, EnumIter, EnumString};

Expand All @@ -7,6 +8,7 @@ pub enum ValueMethod {
Clone,
ToMem,
AbiEncode,
Create,
}

#[derive(
Expand All @@ -20,14 +22,12 @@ pub enum GlobalFunction {
#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumString, AsRefStr)]
#[strum(serialize_all = "snake_case")]
pub enum ContractTypeMethod {
Create,
Create2,
}

impl ContractTypeMethod {
pub fn arg_count(&self) -> usize {
match self {
ContractTypeMethod::Create => 2,
ContractTypeMethod::Create2 => 3,
}
}
Expand Down
5 changes: 3 additions & 2 deletions crates/analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ impl Location {
pub struct FunctionBody {
pub expressions: IndexMap<NodeId, ExpressionAttributes>,
// Map match statements to the corresponding [`PatternMatrix`]
pub types: IndexMap<NodeId, TypeId>,
pub matches: IndexMap<NodeId, PatternMatrix>,
// Map lhs of variable declaration to type.
pub var_types: IndexMap<NodeId, TypeId>,
Expand Down Expand Up @@ -566,8 +567,8 @@ 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
if let Type::Struct(struct_) = type_id.typ(db) {
struct_.name(db) == "Context" && struct_.module(db).ingot(db).name(db) == "std"
if let Some(struct_id) = type_id.as_struct(db) {
struct_id.is_std_context(db)
} else {
false
}
Expand Down
4 changes: 4 additions & 0 deletions crates/analyzer/src/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,10 @@ impl StructId {
db.struct_all_functions(*self)
}

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

pub fn functions(&self, db: &dyn AnalyzerDb) -> Rc<IndexMap<SmolStr, FunctionId>> {
db.struct_function_map(*self).value
}
Expand Down
13 changes: 13 additions & 0 deletions crates/analyzer/src/namespace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ impl TypeId {
pub fn as_struct(&self, db: &dyn AnalyzerDb) -> Option<StructId> {
self.typ(db).as_struct()
}
pub fn as_contract(&self, db: &dyn AnalyzerDb) -> Option<ContractId> {
self.typ(db).as_contract()
}

pub fn name(&self, db: &dyn AnalyzerDb) -> SmolStr {
self.typ(db).name(db)
Expand Down Expand Up @@ -582,6 +585,7 @@ pub trait TypeDowncast {
fn as_primitive(&self) -> Option<Base>;
fn as_generic(&self) -> Option<&Generic>;
fn as_struct(&self) -> Option<StructId>;
fn as_contract(&self) -> Option<ContractId>;
}

impl TypeDowncast for Type {
Expand Down Expand Up @@ -627,6 +631,12 @@ impl TypeDowncast for Type {
_ => None,
}
}
fn as_contract(&self) -> Option<ContractId> {
match self {
Type::Contract(id) => Some(*id),
_ => None,
}
}
fn as_generic(&self) -> Option<&Generic> {
match self {
Type::Generic(inner) => Some(inner),
Expand Down Expand Up @@ -657,6 +667,9 @@ impl TypeDowncast for Option<&Type> {
fn as_struct(&self) -> Option<StructId> {
self.and_then(TypeDowncast::as_struct)
}
fn as_contract(&self) -> Option<ContractId> {
self.and_then(TypeDowncast::as_contract)
}
fn as_generic(&self) -> Option<&Generic> {
self.and_then(TypeDowncast::as_generic)
}
Expand Down
66 changes: 55 additions & 11 deletions crates/analyzer/src/traversal/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use crate::display::Displayable;
use crate::errors::{FatalError, IndexingError};
use crate::namespace::items::{FunctionId, FunctionSigId, ImplId, Item, StructId, TypeDef};
use crate::namespace::scopes::BlockScopeType;
use crate::namespace::types::{Array, Base, FeString, Integer, Tuple, Type, TypeDowncast, TypeId};
use crate::namespace::types::{
Array, Base, FeString, FunctionParam, Integer, Tuple, Type, TypeDowncast, TypeId,
};
use crate::operations;
use crate::traversal::call_args::{validate_arg_count, validate_named_args};
use crate::traversal::const_expr::eval_expr;
Expand All @@ -19,16 +21,19 @@ use crate::{

use fe_common::diagnostics::Label;
use fe_common::{numeric, Span};
use fe_parser::ast as fe;
use fe_parser::ast::GenericArg;
use fe_parser::node::Node;
// cleanup
use fe_parser::{ast as fe, ast};
use num_bigint::BigInt;
use num_traits::ToPrimitive;
use smol_str::SmolStr;
use std::ops::RangeInclusive;
use std::rc::Rc;
use std::str::FromStr;

use super::types::type_desc;

/// Gather context information for expressions and check for type errors.
pub fn expr(
context: &mut dyn AnalyzerContext,
Expand Down Expand Up @@ -1509,6 +1514,7 @@ fn expr_call_method(
target,
method,
field,
generic_args,
args,
);
}
Expand Down Expand Up @@ -1702,17 +1708,28 @@ fn expr_call_builtin_value_method(
value: &Node<fe::Expr>,
method: ValueMethod,
method_name: &Node<SmolStr>,
generic_args: &Option<Node<Vec<fe::GenericArg>>>,
args: &Node<Vec<Node<fe::CallArg>>>,
) -> Result<(ExpressionAttributes, CallType), FatalError> {
// for now all of these functions expect 0 arguments
validate_arg_count(
context,
&method_name.kind,
method_name.span,
args,
0,
"argument",
);
if method == ValueMethod::Create {
validate_arg_count(
context,
&method_name.kind,
method_name.span,
args,
2,
"argument",
);
} else {
validate_arg_count(
context,
&method_name.kind,
method_name.span,
args,
0,
"argument",
);
}

let calltype = CallType::BuiltinValueMethod {
method,
Expand Down Expand Up @@ -1857,6 +1874,33 @@ fn expr_call_builtin_value_method(
],
))),
},
ValueMethod::Create => {
if let GenericArg::TypeDesc(desc) =
generic_args.as_ref().unwrap().kind.get(0).clone().unwrap()
{
let typ = type_desc(context, &desc)?;
let args_type = context
.db()
.intern_type(Type::unit());
// .intern_type(Type::Tuple(Tuple { items: Rc::new([]) }));
let value = context
.db()
.intern_type(Type::Base(Base::u256()));
validate_named_args(
context,
&method_name.kind,
method_name.span,
&args,
&[
FunctionParam::new(None, "args", Ok(args_type)),
FunctionParam::new(None, "value", Ok(value)),
],
)?;
Ok((ExpressionAttributes::new(typ, Location::Value), calltype))
} else {
panic!("shit")
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/analyzer/tests/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ test_file! { call_non_pub_fn_on_struct }
test_file! { call_non_pub_fn_on_struct2 }
test_file! { cannot_move }
test_file! { cannot_move2 }
test_file! { circular_dependency_create }
// test_file! { circular_dependency_create }
test_file! { circular_dependency_create2 }
test_file! { circular_type_alias }
test_file! { const_assign }
Expand Down
20 changes: 10 additions & 10 deletions crates/analyzer/tests/snapshots/analysis__basic_ingot.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: snapshot

---
note:
┌─ ingots/basic_ingot/src/main.fe:9:5
Expand Down Expand Up @@ -168,30 +167,31 @@ note:
┌─ ingots/basic_ingot/src/main.fe:35:5
35 │ ╭ pub fn create_bing_contract(ctx: Context) -> u256 {
36 │ │ let bing_contract: BingContract = BingContract.create(ctx, 0)
36 │ │ let bing_contract: BingContract = ctx.create<BingContract>(args: (), value: 0)
37 │ │ return bing_contract.add(40, 50)
38 │ │ }
│ ╰─────^ self: None, params: [{ label: None, name: ctx, typ: Context }] -> u256

note:
┌─ ingots/basic_ingot/src/main.fe:36:13
36 │ let bing_contract: BingContract = BingContract.create(ctx, 0)
36 │ let bing_contract: BingContract = ctx.create<BingContract>(args: (), value: 0)
│ ^^^^^^^^^^^^^ BingContract

note:
┌─ ingots/basic_ingot/src/main.fe:36:63
┌─ ingots/basic_ingot/src/main.fe:36:43
36 │ let bing_contract: BingContract = BingContract.create(ctx, 0)
│ ^^^ ^ u256: Value
│ │
│ Context: Memory
36 │ let bing_contract: BingContract = ctx.create<BingContract>(args: (), value: 0)
│ ^^^ ^^ ^ u256: Value
│ │ │
│ │ (): Value
│ Context: Memory

note:
┌─ ingots/basic_ingot/src/main.fe:36:43
36 │ let bing_contract: BingContract = BingContract.create(ctx, 0)
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ BingContract: Value
36 │ let bing_contract: BingContract = ctx.create<BingContract>(args: (), value: 0)
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ BingContract: Value
37 │ return bing_contract.add(40, 50)
│ ^^^^^^^^^^^^^ ^^ ^^ u256: Value
│ │ │
Expand Down
20 changes: 10 additions & 10 deletions crates/analyzer/tests/snapshots/analysis__create_contract.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ create_contract.fe:2:5
Expand All @@ -21,30 +20,31 @@ note:
┌─ create_contract.fe:8:5
8 │ ╭ pub fn create_foo(ctx: Context) -> address {
9 │ │ let foo: Foo = Foo.create(ctx, 0)
9 │ │ let foo: Foo = ctx.create<Foo>(args: (), value: 0)
10 │ │ return address(foo)
11 │ │ }
│ ╰─────^ self: None, params: [{ label: None, name: ctx, typ: Context }] -> address

note:
┌─ create_contract.fe:9:13
9 │ let foo: Foo = Foo.create(ctx, 0)
9 │ let foo: Foo = ctx.create<Foo>(args: (), value: 0)
│ ^^^ Foo

note:
┌─ create_contract.fe:9:35
┌─ create_contract.fe:9:24
9 │ let foo: Foo = Foo.create(ctx, 0)
│ ^^^ ^ u256: Value
│ │
│ Context: Memory
9 │ let foo: Foo = ctx.create<Foo>(args: (), value: 0)
│ ^^^ ^^ ^ u256: Value
│ │ │
│ │ (): Value
│ Context: Memory

note:
┌─ create_contract.fe:9:24
9 │ let foo: Foo = Foo.create(ctx, 0)
│ ^^^^^^^^^^^^^^^^^^ Foo: Value
9 │ let foo: Foo = ctx.create<Foo>(args: (), value: 0)
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Foo: Value
10 │ return address(foo)
│ ^^^ Foo: Value

Expand Down
7 changes: 6 additions & 1 deletion crates/analyzer/tests/snapshots/errors__bad_visibility.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/errors.rs
expression: error_string_ingot(&path)

---
error: unresolved path item
┌─ compile_errors/bad_visibility/src/main.fe:1:68
Expand Down Expand Up @@ -143,6 +142,12 @@ error: the type `MyContract` is private
= `MyContract` can only be used within `foo`
= Hint: use `pub` to make `MyContract` visible from outside of `foo`

error: No function `create` exists on type `MyContract`
┌─ compile_errors/bad_visibility/src/main.fe:26:20
26 │ MyContract.create(ctx, 1)
│ ^^^^^^ undefined function

error: the trait `MyTrait` is private
┌─ compile_errors/bad_visibility/src/foo.fe:5:7
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
---
source: crates/analyzer/tests/errors.rs
expression: "error_string(&path, test_files::fixture(path))"

---
error: incorrect type for argument to `Bar.create`
┌─ compile_errors/call_create_with_wrong_type.fe:5:25
error: No function `create` exists on type `Bar`
┌─ compile_errors/call_create_with_wrong_type.fe:5:13
5 │ Bar.create(ctx, true)
│ ^^^^ this has type `bool`; expected a number

error: `create` expects 2 arguments, but 1 was provided
┌─ compile_errors/call_create_with_wrong_type.fe:6:13
6 │ Bar.create(ctx) // agroce //447
│ ^^^^^^ --- supplied 1 argument
│ │
│ expects 2 arguments
│ ^^^^^^ undefined function


Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
---
source: crates/analyzer/tests/errors.rs
expression: "error_string(&path, test_files::fixture(path))"

---
error: `Foo.create(...)` called within `Foo` creates an illegal circular dependency
┌─ compile_errors/circular_dependency_create.fe:3:28
3 │ let foo: Foo = Foo.create(ctx, 0)
│ ^^^^^^ Contract creation
error: No function `create` exists on type `Foo`
┌─ compile_errors/circular_dependency_create.fe:5:28
= Note: Consider using a dedicated factory contract to create instances of `Foo`
5 │ let foo: Foo = Foo.create(ctx, 0)
│ ^^^^^^ undefined function


Loading