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

Add Option and Result variants in prelude #4504

Merged
merged 2 commits into from
Apr 28, 2023
Merged
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 docs/book/src/advanced/generic_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,15 @@ Similar to Rust, Sway has what is colloquially known as the [turbofish](https://

```sway
fn foo<T, E>(t: T) -> Result<T, E> {
Result::Ok(t)
Ok(t)
}
```

In this code example, which is admittedly asinine, you can't possibly know what type `E` is. You'd need to provide the type manually, with a turbofish:

```sway
fn foo<T, E>(t: T) -> Result<T, E> {
Result::Ok::<T, MyErrorType>(t)
Ok::<T, MyErrorType>(t)
}
```

Expand Down
6 changes: 3 additions & 3 deletions docs/book/src/blockchain-development/access_control.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ To deliver an experience akin to the EVM's access control, the `std` library pro

The `msg_sender` function works as follows:

- If the caller is a contract, then `Result::Ok(Sender)` is returned with the `ContractId` sender variant.
- If the caller is external (i.e. from a script), then all coin input owners in the transaction are checked. If all owners are the same, then `Result::Ok(Sender)` is returned with the `Address` sender variant.
- If the caller is external and coin input owners are different, then the caller cannot be determined and a `Result::Err(AuthError)` is returned.
- If the caller is a contract, then `Ok(Sender)` is returned with the `ContractId` sender variant.
- If the caller is external (i.e. from a script), then all coin input owners in the transaction are checked. If all owners are the same, then `Ok(Sender)` is returned with the `Address` sender variant.
- If the caller is external and coin input owners are different, then the caller cannot be determined and a `Err(AuthError)` is returned.

## Contract Ownership

Expand Down
2 changes: 1 addition & 1 deletion docs/book/src/common-collections/storage_map.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ We can get a value out of the storage map by providing its `key` to the `get` me
{{#include ../../../../examples/storage_map/src/main.sw:storage_map_get}}
```

Here, `value1` will have the value that's associated with the first address, and the result will be `42`. The `get` method returns an `Option<V>`; if there’s no value for that key in the storage map, `get` will return `Option::None`. This program handles the `Option` by calling `unwrap_or` to set `value1` to zero if `map` doesn't have an entry for the key.
Here, `value1` will have the value that's associated with the first address, and the result will be `42`. The `get` method returns an `Option<V>`; if there’s no value for that key in the storage map, `get` will return `None`. This program handles the `Option` by calling `unwrap_or` to set `value1` to zero if `map` doesn't have an entry for the key.

## Storage Maps with Multiple Keys

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ contract;

// ANCHOR: identity
storage {
owner: Option<Identity> = Option::None,
owner: Option<Identity> = None,
}

// ANCHOR_END: identity
Expand Down
8 changes: 6 additions & 2 deletions docs/reference/src/documentation/misc/prelude.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ The prelude contains the following:
- [`Identity`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/identity.sw): An enum containing `Address` & `ContractID` structs
- [`Vec`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/vec.sw): A growable, heap-allocated vector
- [`StorageMap`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/storage.sw): A key-value mapping in contract storage
- [`Option`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/option.sw): An enum containing either some generic value `<T>` or an absence of that value
- [`Result`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/result.sw): An enum used to represent either a success or failure of an operation
- [`Option`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/option.sw): An enum containing either some generic value `<T>` or an absence of that value, we also expose the variants directly:
- `Some`
- `None`
- [`Result`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/result.sw): An enum used to represent either a success or failure of an operation, we also expose the variants directly:
- `Ok`
- `Err`
- [`assert`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/assert.sw): A module containing
- `assert`: A function that reverts the VM if the condition provided to it is false
- `assert_eq`: A function that reverts the VM and logs its two inputs v1 and v2 if the condition v1 == v2 is false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ There are two `storage` variables: `balance` & `user`. `balance` takes a single

## Reading from Storage

Retrieving data from a storage variable is done through the `.get(key)` method. That is to say that we state which storage variable we would like to read from and append `.get()` to the end while providing the key for the data that we want to retrieve. The method `get` returns an `Option`; if there is no value for `key` in the map, `get` will return `Option::None`.
Retrieving data from a storage variable is done through the `.get(key)` method. That is to say that we state which storage variable we would like to read from and append `.get()` to the end while providing the key for the data that we want to retrieve. The method `get` returns an `Option`; if there is no value for `key` in the map, `get` will return `None`.

In this example we wrap the [`Identity`](../../namespace/identity.md) of the caller with their provided `id` into a [tuple](../../../language/built-ins/tuples.md) and use that as the key.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ In this example we retrieve some `u64` at the position of `key`.
{{#include ../../../../code/operations/storage/store_get/src/main.sw:get}}
```

The function `get` returns an `Option`; if the storage slots requested have not been set before, `get` will return `Option::None`.
The function `get` returns an `Option`; if the storage slots requested have not been set before, `get` will return `None`.
8 changes: 4 additions & 4 deletions examples/option/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ script;

fn divide(numerator: u64, denominator: u64) -> Option<u64> {
if denominator == 0 {
Option::None
None
} else {
Option::Some(numerator / denominator)
Some(numerator / denominator)
}
}

Expand All @@ -13,8 +13,8 @@ fn main() {
// Pattern match to retrieve the value
match result {
// The division was valid
Option::Some(x) => std::logging::log(x),
Some(x) => std::logging::log(x),
// The division was invalid
Option::None => std::logging::log("Cannot divide by 0"),
None => std::logging::log("Cannot divide by 0"),
}
}
6 changes: 3 additions & 3 deletions examples/ownership/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ abi OwnershipExample {
}

storage {
owner: Option<Identity> = Option::None,
owner: Option<Identity> = None,
}

impl OwnershipExample for Contract {
// ANCHOR: revoke_owner_example
#[storage(write)]
fn revoke_ownership() {
storage.owner.write(Option::None);
storage.owner.write(None);
}
// ANCHOR_END: revoke_owner_example
// ANCHOR: set_owner_example
#[storage(write)]
fn set_owner(identity: Identity) {
storage.owner.write(Option::Some(identity));
storage.owner.write(Some(identity));
}
// ANCHOR_END: set_owner_example
#[storage(read)]
Expand Down
8 changes: 4 additions & 4 deletions examples/result/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ enum MyContractError {

fn divide(numerator: u64, denominator: u64) -> Result<u64, MyContractError> {
if (denominator == 0) {
return Result::Err(MyContractError::DivisionByZero);
return Err(MyContractError::DivisionByZero);
} else {
Result::Ok(numerator / denominator)
Ok(numerator / denominator)
}
}

fn main() -> Result<u64, str[4]> {
let result = divide(20, 2);
match result {
Result::Ok(value) => Result::Ok(value),
Result::Err(MyContractError::DivisionByZero) => Result::Err("Fail"),
Ok(value) => Ok(value),
Err(MyContractError::DivisionByZero) => Err("Fail"),
}
}
2 changes: 1 addition & 1 deletion examples/signatures/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn main() {

// A recovered Fuel address.
let result_address: Result<Address, EcRecoverError> = ec_recover_address(signature, MSG_HASH);
if let Result::Ok(address) = result_address {
if let Ok(address) = result_address {
log(address.value);
} else {
revert(0);
Expand Down
4 changes: 2 additions & 2 deletions examples/storage_vec/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ impl StorageVecContract for Contract {
fn read_from_storage_vec() {
let third = storage.v.get(2);
match third {
Option::Some(third) => log(third.read()),
Option::None => revert(42),
Some(third) => log(third.read()),
None => revert(42),
}
}
// ANCHOR_END: storage_vec_get
Expand Down
4 changes: 2 additions & 2 deletions examples/vec/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ fn main() {
// ANCHOR: vec_get
let third = v.get(2);
match third {
Option::Some(third) => log(third),
Option::None => revert(42),
Some(third) => log(third),
None => revert(42),
}
// ANCHOR_END: vec_get
// ANCHOR: vec_get_oob
Expand Down
3 changes: 3 additions & 0 deletions sway-ast/src/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub enum Pattern {
Wildcard {
underscore_token: UnderscoreToken,
},
/// A pattern made of a single ident, which could either be a variable or an enum variant
AmbiguousSingleIdent(Ident),
Var {
reference: Option<RefToken>,
mutable: Option<MutToken>,
Expand Down Expand Up @@ -51,6 +53,7 @@ impl Spanned for Pattern {
(None, Some(mut_token)) => Span::join(mut_token.span(), name.span()),
(None, None) => name.span(),
},
Pattern::AmbiguousSingleIdent(ident) => ident.span(),
Pattern::Literal(literal) => literal.span(),
Pattern::Constant(path_expr) => path_expr.span(),
Pattern::Constructor { path, args } => Span::join(path.span(), args.span()),
Expand Down
4 changes: 3 additions & 1 deletion sway-core/src/language/parsed/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,12 @@ pub enum ExpressionKind {
Error(Box<[Span]>),
Literal(Literal),
/// An ambiguous path where we don't know until type checking whether this
/// is a free function call or a UFCS (Rust term) style associated function call.
/// is a free function call, an enum variant or a UFCS (Rust term) style associated function call.
AmbiguousPathExpression(Box<AmbiguousPathExpression>),
FunctionApplication(Box<FunctionApplicationExpression>),
LazyOperator(LazyOperatorExpression),
/// And ambiguous single ident which could either be a variable or an enum variant
AmbiguousVariableExpression(Ident),
Variable(Ident),
Tuple(Vec<Expression>),
TupleIndex(TupleIndexExpression),
Expand Down
3 changes: 3 additions & 0 deletions sway-core/src/language/parsed/expression/scrutinee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum Scrutinee {
name: Ident,
span: Span,
},
AmbiguousSingleIdent(Ident),
StructScrutinee {
struct_name: CallPath,
fields: Vec<StructScrutineeField>,
Expand Down Expand Up @@ -66,6 +67,7 @@ impl Spanned for Scrutinee {
Scrutinee::CatchAll { span } => span.clone(),
Scrutinee::Literal { span, .. } => span.clone(),
Scrutinee::Variable { span, .. } => span.clone(),
Scrutinee::AmbiguousSingleIdent(ident) => ident.span(),
Scrutinee::StructScrutinee { span, .. } => span.clone(),
Scrutinee::EnumScrutinee { span, .. } => span.clone(),
Scrutinee::Tuple { span, .. } => span.clone(),
Expand Down Expand Up @@ -168,6 +170,7 @@ impl Scrutinee {
.collect::<Vec<TypeInfo>>(),
Scrutinee::Literal { .. }
| Scrutinee::CatchAll { .. }
| Scrutinee::AmbiguousSingleIdent(..)
| Scrutinee::Variable { .. }
| Scrutinee::Error { .. } => {
vec![]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,27 @@ impl ty::TyScrutinee {
value,
span,
} => type_check_enum(ctx, call_path, *value, span),
Scrutinee::AmbiguousSingleIdent(ident) => {
let maybe_enum = type_check_enum(
ctx.by_ref(),
CallPath {
prefixes: vec![],
suffix: ident.clone(),
is_absolute: false,
},
Scrutinee::Tuple {
elems: vec![],
span: ident.span(),
},
ident.span(),
);

if maybe_enum.is_ok() {
maybe_enum
} else {
type_check_variable(ctx, ident.clone(), ident.span())
}
}
Scrutinee::Tuple { elems, span } => type_check_tuple(ctx, elems, span),
Scrutinee::Error { .. } => err(vec![], vec![]),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,30 @@ impl ty::TyExpression {
// We've already emitted an error for the `::Error` case.
ExpressionKind::Error(_) => ok(ty::TyExpression::error(span, engines), vec![], vec![]),
ExpressionKind::Literal(lit) => Self::type_check_literal(engines, lit, span),
ExpressionKind::AmbiguousVariableExpression(name) => {
let call_path = CallPath {
prefixes: vec![],
suffix: name.clone(),
is_absolute: false,
};
if matches!(
ctx.namespace.resolve_call_path(&call_path).value,
Some(ty::TyDecl::EnumVariantDecl { .. })
) {
Self::type_check_delineated_path(
ctx.by_ref(),
TypeBinding {
span: call_path.span(),
inner: call_path,
type_arguments: TypeArgs::Regular(vec![]),
},
span,
None,
)
} else {
Self::type_check_variable_expression(ctx.by_ref(), name, span)
}
}
ExpressionKind::Variable(name) => {
Self::type_check_variable_expression(ctx.by_ref(), name, span)
}
Expand Down
3 changes: 3 additions & 0 deletions sway-core/src/semantic_analysis/node_dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,9 @@ impl Dependencies {
// ordered
self.gather_from_call_path(&(name.clone()).into(), false, false)
}
ExpressionKind::AmbiguousVariableExpression(name) => {
self.gather_from_call_path(&(name.clone()).into(), false, false)
}
ExpressionKind::FunctionApplication(function_application_expression) => {
let FunctionApplicationExpression {
call_path_binding,
Expand Down
14 changes: 11 additions & 3 deletions sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2366,6 +2366,7 @@ fn fn_arg_to_function_parameter(
mutable,
name,
} => (reference, mutable, name),
Pattern::AmbiguousSingleIdent(ident) => (None, None, ident),
Pattern::Literal(..) => {
let error = ConvertParseTreeError::LiteralPatternsNotSupportedHere { span: pat_span };
return Err(handler.emit_err(error.into()));
Expand Down Expand Up @@ -2541,10 +2542,15 @@ fn path_expr_to_expression(
path_expr: PathExpr,
) -> Result<Expression, ErrorEmitted> {
let span = path_expr.span();
let expression = if path_expr.root_opt.is_none() && path_expr.suffix.is_empty() {
let expression = if path_expr.root_opt.is_none()
&& path_expr.suffix.is_empty()
&& path_expr.prefix.generics_opt.is_none()
{
// only `foo`, it coult either be a variable or an enum variant

let name = path_expr_segment_to_ident(context, handler, &path_expr.prefix)?;
Expression {
kind: ExpressionKind::Variable(name),
kind: ExpressionKind::AmbiguousVariableExpression(name),
span,
}
} else {
Expand Down Expand Up @@ -3076,14 +3082,15 @@ fn statement_let_to_ast_nodes(
span: Span,
) -> Result<Vec<AstNode>, ErrorEmitted> {
let ast_nodes = match pattern {
Pattern::Wildcard { .. } | Pattern::Var { .. } => {
Pattern::Wildcard { .. } | Pattern::Var { .. } | Pattern::AmbiguousSingleIdent(..) => {
let (reference, mutable, name) = match pattern {
Pattern::Var {
reference,
mutable,
name,
} => (reference, mutable, name),
Pattern::Wildcard { .. } => (None, None, Ident::new_no_span("_".into())),
Pattern::AmbiguousSingleIdent(ident) => (None, None, ident),
_ => unreachable!(),
};
if reference.is_some() {
Expand Down Expand Up @@ -3474,6 +3481,7 @@ fn pattern_to_scrutinee(
}
Scrutinee::Variable { name, span }
}
Pattern::AmbiguousSingleIdent(ident) => Scrutinee::AmbiguousSingleIdent(ident),
Pattern::Literal(literal) => Scrutinee::Literal {
value: literal_to_literal(context, handler, literal)?,
span,
Expand Down
Loading