Skip to content

Commit

Permalink
Blanket trait implementations (FuelLabs#4687)
Browse files Browse the repository at this point in the history
## Description

This change allows blanket implementations for traits, i.e.:
```sway
impl<T> SomeTrait for T {}
```
As part of this we now import trait implementations that are in the same
namespace as an imported trait.

Ideally we'd want to import all existing trait implementations this way,
but this would require another pass or a query, which is probably out of
scope for a first implementation.

Traits are deduplicated here using the trait decl span.

Fix FuelLabs#4010

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

Co-authored-by: Joshua Batty <[email protected]>
  • Loading branch information
IGI-111 and JoshuaBatty authored Jun 23, 2023
1 parent e8d6472 commit 8d467e6
Show file tree
Hide file tree
Showing 15 changed files with 127 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ impl ty::TyDecl {
impl_trait.implementing_for.type_id,
&impl_trait.items,
&impl_trait.span,
impl_trait
.trait_decl_ref
.as_ref()
.map(|decl_ref| decl_ref.decl_span().clone()),
false,
engines,
),
Expand Down Expand Up @@ -216,6 +220,10 @@ impl ty::TyDecl {
impl_trait.implementing_for.type_id,
&impl_trait.items,
&impl_trait.span,
impl_trait
.trait_decl_ref
.as_ref()
.map(|decl_ref| decl_ref.decl_span().clone()),
true,
engines,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ impl ty::TyImplTrait {
&trait_decl.items,
&items,
&trait_name,
&trait_decl.span(),
&block_span,
false,
),
Expand Down Expand Up @@ -199,6 +200,7 @@ impl ty::TyImplTrait {
&abi.items,
&items,
&trait_name,
&abi.span(),
&block_span,
true
),
Expand Down Expand Up @@ -394,6 +396,7 @@ fn type_check_trait_implementation(
trait_items: &[TyImplItem],
impl_items: &[ImplItem],
trait_name: &CallPath,
trait_decl_span: &Span,
block_span: &Span,
is_contract: bool,
) -> CompileResult<Vec<TyImplItem>> {
Expand Down Expand Up @@ -481,6 +484,7 @@ fn type_check_trait_implementation(
.cloned()
.collect::<Vec<_>>(),
&trait_name.span(),
Some(trait_decl_span.clone()),
false,
engines,
);
Expand Down
2 changes: 2 additions & 0 deletions sway-core/src/semantic_analysis/ast_node/declaration/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ impl ty::TyTraitDecl {
self_type,
&dummy_interface_surface,
&span,
None,
false,
engines,
),
Expand Down Expand Up @@ -409,6 +410,7 @@ impl ty::TyTraitDecl {
type_id,
&all_items,
&trait_name.span(),
Some(self.span()),
false,
engines,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@ impl ty::TyExpression {
return_type,
&abi_items,
&span,
Some(span.clone()),
false,
engines,
),
Expand Down
12 changes: 12 additions & 0 deletions sway-core/src/semantic_analysis/namespace/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,18 @@ impl Module {
engines,
);
}
// if this is a trait, import its implementations
let decl_span = decl.span();
if let TyDecl::TraitDecl(_) = &decl {
// TODO: we only import local impls from the source namespace
// this is okay for now but we'll need to device some mechanism to collect all available trait impls
impls_to_insert.extend(
src_ns
.implemented_traits
.filter_by_trait_decl_span(decl_span),
engines,
);
}
// no matter what, import it this way though.
let dst_ns = &mut self[dst];
let add_synonym = |name| {
Expand Down
7 changes: 3 additions & 4 deletions sway-core/src/semantic_analysis/namespace/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ use crate::{
decl_engine::{DeclRefConstant, DeclRefFunction},
engine_threading::*,
error::*,
language::{
ty::{self},
CallPath, Visibility,
},
language::{ty, CallPath, Visibility},
type_system::*,
CompileResult, Ident,
};
Expand Down Expand Up @@ -616,6 +613,7 @@ impl Namespace {
type_id: TypeId,
items: &[ty::TyImplItem],
impl_span: &Span,
trait_decl_span: Option<Span>,
is_impl_self: bool,
engines: &Engines,
) -> CompileResult<()> {
Expand All @@ -629,6 +627,7 @@ impl Namespace {
type_id,
items,
impl_span,
trait_decl_span,
is_impl_self,
engines,
)
Expand Down
34 changes: 33 additions & 1 deletion sway-core/src/semantic_analysis/namespace/trait_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type TraitName = CallPath<TraitSuffix>;
struct TraitKey {
name: TraitName,
type_id: TypeId,
trait_decl_span: Option<Span>,
}

impl OrdWithEngines for TraitKey {
Expand Down Expand Up @@ -112,6 +113,7 @@ impl TraitMap {
type_id: TypeId,
items: &[TyImplItem],
impl_span: &Span,
trait_decl_span: Option<Span>,
is_impl_self: bool,
engines: &Engines,
) -> CompileResult<()> {
Expand Down Expand Up @@ -150,6 +152,7 @@ impl TraitMap {
TraitKey {
name: map_trait_name,
type_id: map_type_id,
trait_decl_span: _,
},
value:
TraitValue {
Expand Down Expand Up @@ -241,7 +244,14 @@ impl TraitMap {
};

// even if there is a conflicting definition, add the trait anyway
self.insert_inner(trait_name, impl_span.clone(), type_id, trait_items, engines);
self.insert_inner(
trait_name,
impl_span.clone(),
trait_decl_span,
type_id,
trait_items,
engines,
);

if errors.is_empty() {
ok((), warnings, errors)
Expand All @@ -254,13 +264,15 @@ impl TraitMap {
&mut self,
trait_name: TraitName,
impl_span: Span,
trait_decl_span: Option<Span>,
type_id: TypeId,
trait_methods: TraitItems,
engines: &Engines,
) {
let key = TraitKey {
name: trait_name,
type_id,
trait_decl_span,
};
let value = TraitValue {
trait_items: trait_methods,
Expand Down Expand Up @@ -381,6 +393,23 @@ impl TraitMap {
}
}

/// Filters the entries in `self` and return a new [TraitMap] with all of
/// the entries from `self` that implement a trait from the declaration with that span.
pub(crate) fn filter_by_trait_decl_span(&self, trait_decl_span: Span) -> TraitMap {
let mut trait_map = TraitMap::default();
for entry in self.trait_impls.iter() {
if entry
.key
.trait_decl_span
.as_ref()
.map_or(false, |span| span == &trait_decl_span)
{
trait_map.trait_impls.push(entry.clone());
}
}
trait_map
}

/// Filters the entries in `self` with the given [TypeId] `type_id` and
/// return a new [TraitMap] with all of the entries from `self` for which
/// `type_id` is a subtype. Additionally, the new [TraitMap] contains the
Expand Down Expand Up @@ -603,6 +632,7 @@ impl TraitMap {
TraitKey {
name: map_trait_name,
type_id: map_type_id,
trait_decl_span: map_trait_decl_span,
},
value:
TraitValue {
Expand All @@ -617,6 +647,7 @@ impl TraitMap {
trait_map.insert_inner(
map_trait_name.clone(),
impl_span.clone(),
map_trait_decl_span.clone(),
*type_id,
map_trait_items.clone(),
engines,
Expand Down Expand Up @@ -655,6 +686,7 @@ impl TraitMap {
trait_map.insert_inner(
map_trait_name.clone(),
impl_span.clone(),
map_trait_decl_span.clone(),
*type_id,
trait_items,
engines,
Expand Down
4 changes: 2 additions & 2 deletions sway-core/src/type_system/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1058,9 +1058,9 @@ impl TypeInfo {
| TypeInfo::Array(_, _)
| TypeInfo::Contract
| TypeInfo::Numeric
| TypeInfo::Alias { .. } => ok((), warnings, errors),
| TypeInfo::Alias { .. }
| TypeInfo::UnknownGeneric { .. } => ok((), warnings, errors),
TypeInfo::Unknown
| TypeInfo::UnknownGeneric { .. }
| TypeInfo::ContractCaller { .. }
| TypeInfo::SelfType
| TypeInfo::Storage { .. }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ category = "fail"
# check: $()}
# check: $()Conflicting implementations of trait "Multiple<u64>" for type "FooBarData<F>".

# check: $()impl<T> Returner<T> for T {
# check: $()Unimplemented feature: implementing traits on this type is unsupported right now

# check: $()impl<T> Returner<T> for Self {
# check: $()Unimplemented feature: implementing traits on this type is unsupported right now

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[package]]
name = 'blanket_impl'
source = 'member'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
name = "blanket_impl"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"configurables": [],
"functions": [
{
"attributes": null,
"inputs": [],
"name": "main",
"output": {
"name": "",
"type": 0,
"typeArguments": null
}
}
],
"loggedTypes": [],
"messagesTypes": [],
"types": [
{
"components": null,
"type": "u64",
"typeId": 0,
"typeParameters": null
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
script;

mod traits;

use traits::Foo;

struct Bar {}

fn main() -> u64 {
let bar = Bar {};
bar.foo()

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
library;

pub trait Foo {
fn foo(self) -> u64;
}

impl<T> Foo for T {
fn foo(self) -> u64 {
42
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
category = "run"
expected_result = { action = "return", value = 42 }
validate_abi = true

0 comments on commit 8d467e6

Please sign in to comment.