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

Implement marker traits #6871

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
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
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,10 @@ jobs:
run: cargo run --locked --release -p forc -- build --experimental storage_domains --release --locked --path ./test/src/sdk-harness
- name: Cargo Test sway-lib-std - Experimental Feature 'storage_domains'
run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture
- name: Build All Tests - Experimental Feature 'error_type'
run: cargo run --locked --release -p forc -- build --experimental error_type --release --locked --path ./test/src/sdk-harness
- name: Cargo Test sway-lib-std - Experimental Feature 'error_type'
run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture

forc-run-benchmarks:
runs-on: buildjet-4vcpu-ubuntu-2204
Expand Down Expand Up @@ -541,14 +545,20 @@ jobs:
run: forc build --path sway-lib-core && forc test --path sway-lib-core
- name: Run Core Unit Tests - Experimental feature 'storage_domains'
run: forc build --experimental storage_domains --path sway-lib-core && forc test --experimental storage_domains --path sway-lib-core
- name: Run Core Unit Tests - Experimental feature 'error_type'
run: forc build --experimental error_type --path sway-lib-core && forc test --experimental error_type --path sway-lib-core
- name: Run Std Unit Tests
run: forc build --path sway-lib-std && forc test --path sway-lib-std
- name: Run Std Unit Tests - Experimental feature 'storage_domains'
run: forc build --experimental storage_domains --path sway-lib-std && forc test --experimental storage_domains --path sway-lib-std
- name: Run Std Unit Tests - Experimental feature 'error_type'
run: forc build --experimental error_type --path sway-lib-std && forc test --experimental error_type --path sway-lib-std
- name: Run In Language Unit Tests
run: forc build --path test/src/in_language_tests && forc test --path test/src/in_language_tests
- name: Run In Language Unit Tests - Experimental feature 'storage_domains'
run: forc build --experimental storage_domains --path test/src/in_language_tests && forc test --experimental storage_domains --path test/src/in_language_tests
- name: Run In Language Unit Tests - Experimental feature 'error_type'
run: forc build --experimental error_type --path test/src/in_language_tests && forc test --experimental error_type --path test/src/in_language_tests

forc-pkg-fuels-deps-check:
runs-on: buildjet-4vcpu-ubuntu-2204
Expand Down
39 changes: 37 additions & 2 deletions docs/book/src/advanced/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,43 @@ trait MyTrait {

Check the `associated types` section on [associated types](./associated_types.md) page.

## Trait Constraints

When writing generic code, you can constraint the choice of types for a generic argument by using the `where` keyword. The `where` keyword specifies which traits the concrete generic parameter must implement. In the below example, the function `expects_some_trait` can be called only if the parameter `t` is of a type that has `SomeTrait` implemented. To call the `expects_both_traits`, parameter `t` must be of a type that implements _both_ `SomeTrait` and `SomeOtherTrait`.

```sway
trait SomeTrait { }
trait SomeOtherTrait { }

fn expects_some_trait<T>(t: T) where T: SomeTrait {
// ...
}

fn expects_some_other_trait<T>(t: T) where T: SomeOtherTrait {
// ...
}

fn expects_both_traits<T>(t: T) where T: SomeTrait + SomeOtherTrait {
// ...
}
```

## Marker Traits

Sway types can be classified in various ways according to their intrinsic properties. These classifications are represented as marker traits. Marker traits are implemented by the compiler and cannot be explicitly implemented in code.

E.g., all types whose instances can be used in the `panic` expression automatically implement the `Error` marker trait. We can use that trait, e.g., to specify that a generic argument must be compatible with the `panic` expression:

```sway
fn panic_with_error<E>(err: E) where E: Error {
panic err;
}
```

> **Note** `panic` expression and error types [have not yet been implemented](https://github.com/FuelLabs/sway/issues/6765)

All marker traits are defined in the `core::marker` module.

## Use Cases

### Custom Types (structs, enums)
Expand All @@ -160,8 +197,6 @@ fn play_game_with_deck<T>(a: Vec<T>) where T: Card {
}
```

> **Note** Trait constraints (i.e. using the `where` keyword) [have not yet been implemented](https://github.com/FuelLabs/sway/issues/970)

Now, if you want to use the function `play_game_with_deck` with your struct, you must implement `Card` for your struct. Note that the following code example assumes a dependency _games_ has been included in the `Forc.toml` file.

```sway
Expand Down
45 changes: 39 additions & 6 deletions sway-core/src/language/call_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
OrdWithEnginesContext, PartialEqWithEngines, PartialEqWithEnginesContext,
},
parsed::QualifiedPathType,
Engines, Ident, Namespace,
Engines, Ident, Namespace, TypeArgument,
};
use serde::{Deserialize, Serialize};
use std::{
Expand Down Expand Up @@ -284,20 +284,26 @@ impl<T: Spanned> Spanned for CallPath<T> {
if self.prefixes.is_empty() {
self.suffix.span()
} else {
let suffix_span = self.suffix.span();
let mut prefixes_spans = self
.prefixes
.iter()
.map(|x| x.span())
//LOC below should be removed when #21 goes in
// Depending on how the call path is constructed, we
// might have a situation that the parts do not belong
// to the same source and do not have the same source id.
// In that case, we will take only the suffix' span, as
// the span for the whole call path. Otherwise, we join
// the spans of all the parts.
.filter(|x| {
Arc::ptr_eq(x.src(), self.suffix.span().src())
&& x.source_id() == self.suffix.span().source_id()
Arc::ptr_eq(x.src(), suffix_span.src())
&& x.source_id() == suffix_span.source_id()
})
.peekable();
if prefixes_spans.peek().is_some() {
Span::join(Span::join_all(prefixes_spans), &self.suffix.span())
Span::join(Span::join_all(prefixes_spans), &suffix_span)
} else {
self.suffix.span()
suffix_span
}
}
}
Expand Down Expand Up @@ -391,6 +397,33 @@ impl CallPath {
}
converted
}

/// Create a string form of the given [CallPath] and zero or more [TypeArgument]s.
/// The returned string is convenient for displaying full names, including generic arguments, in help messages.
/// E.g.:
/// - `some::module::SomeType`
/// - `some::module::SomeGenericType<T, u64>`
///
/// Note that the trailing arguments are never separated by `::` from the suffix.
pub(crate) fn to_string_with_args(&self, engines: &Engines, args: &[TypeArgument]) -> String {
let args = args
.iter()
.map(|type_arg| engines.help_out(type_arg).to_string())
.collect::<Vec<_>>()
.join(", ");

format!(
"{}{}",
// TODO: Replace with a context aware string representation of the path
// once https://github.com/FuelLabs/sway/issues/6873 is fixed.
&self,
if args.is_empty() {
String::new()
} else {
format!("<{args}>")
}
)
}
}

impl<T: Clone> CallPath<T> {
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod debug_generation;
pub mod decl_engine;
pub mod ir_generation;
pub mod language;
pub mod marker_traits;
mod metadata;
pub mod query_engine;
pub mod semantic_analysis;
Expand Down
35 changes: 35 additions & 0 deletions sway-core/src/marker_traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use sway_types::{Ident, SourceEngine};

use crate::{
language::{parsed::ImplSelfOrTrait, ty::TyTraitDecl, CallPathType},
namespace::Module,
};

impl TyTraitDecl {
pub(crate) fn is_marker_trait(&self) -> bool {
assert!(
matches!(self.call_path.callpath_type, CallPathType::Full),
"call paths of trait declarations must always be full paths"
);

is_core_marker_module_path(&self.call_path.prefixes)
}
}

impl Module {
pub(crate) fn is_core_marker_module(&self) -> bool {
is_core_marker_module_path(self.mod_path())
}
}

impl ImplSelfOrTrait {
pub(crate) fn is_autogenerated(&self, source_engine: &SourceEngine) -> bool {
source_engine
.is_span_in_autogenerated(&self.block_span)
.unwrap_or(false)
}
}

fn is_core_marker_module_path(path: &[Ident]) -> bool {
path.len() == 2 && path[0].as_str() == "core" && path[1].as_str() == "marker"
}
Loading
Loading