Skip to content

Commit 6981d3d

Browse files
authored
Import indirect trait impls when handling item use statements. (#7347)
## Description This updates `TraitMap::filter_by_type_item_import` to scan the trait map for indirect trait impls with generic types matching any of imported item types. This makes sure cases like an impl of a `StorageKey<T>` work, when importing T from a given scope. Fixes #7330. ## 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). - [ ] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [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.
1 parent 65d6520 commit 6981d3d

File tree

8 files changed

+227
-1
lines changed

8 files changed

+227
-1
lines changed

sway-core/src/decl_engine/parsed_id.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use super::{
44
};
55
use crate::{
66
engine_threading::{
7-
EqWithEngines, HashWithEngines, PartialEqWithEngines, PartialEqWithEnginesContext,
7+
DebugWithEngines, EqWithEngines, HashWithEngines, PartialEqWithEngines,
8+
PartialEqWithEnginesContext,
89
},
910
Engines,
1011
};
@@ -58,6 +59,17 @@ impl<T> PartialEq for ParsedDeclId<T> {
5859
}
5960
}
6061

62+
impl<T> DebugWithEngines for ParsedDeclId<T>
63+
where
64+
ParsedDeclEngine: ParsedDeclEngineIndex<T>,
65+
T: Named + Spanned + DebugWithEngines,
66+
{
67+
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
68+
let decl = engines.pe().get(self);
69+
DebugWithEngines::fmt(&decl, f, engines)
70+
}
71+
}
72+
6173
impl<T> EqWithEngines for ParsedDeclId<T> {}
6274
impl<T> PartialEqWithEngines for ParsedDeclId<T> {
6375
fn eq(&self, other: &Self, _ctx: &PartialEqWithEnginesContext) -> bool {

sway-core/src/semantic_analysis/namespace/trait_map.rs

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,43 @@ pub(crate) enum TypeRootFilter {
221221
TraitType(String),
222222
}
223223

224+
impl DebugWithEngines for TypeRootFilter {
225+
fn fmt(&self, f: &mut fmt::Formatter<'_>, _engines: &Engines) -> fmt::Result {
226+
use TypeRootFilter::*;
227+
match self {
228+
Unknown => write!(f, "Unknown"),
229+
Never => write!(f, "Never"),
230+
Placeholder => write!(f, "Placeholder"),
231+
StringSlice => write!(f, "StringSlice"),
232+
StringArray => write!(f, "StringArray"),
233+
U8 => write!(f, "u8"),
234+
U16 => write!(f, "u16"),
235+
U32 => write!(f, "u32"),
236+
U64 => write!(f, "u64"),
237+
U256 => write!(f, "u256"),
238+
Bool => write!(f, "bool"),
239+
Custom(name) => write!(f, "Custom({})", name),
240+
B256 => write!(f, "b256"),
241+
Contract => write!(f, "Contract"),
242+
ErrorRecovery => write!(f, "ErrorRecovery"),
243+
Tuple(n) => write!(f, "Tuple(len={})", n),
244+
Enum(parsed_id) => {
245+
write!(f, "Enum({:?})", parsed_id)
246+
}
247+
Struct(parsed_id) => {
248+
write!(f, "Struct({:?})", parsed_id)
249+
}
250+
ContractCaller(abi_name) => write!(f, "ContractCaller({})", abi_name),
251+
Array => write!(f, "Array"),
252+
RawUntypedPtr => write!(f, "RawUntypedPtr"),
253+
RawUntypedSlice => write!(f, "RawUntypedSlice"),
254+
Ptr => write!(f, "Ptr"),
255+
Slice => write!(f, "Slice"),
256+
TraitType(name) => write!(f, "TraitType({})", name),
257+
}
258+
}
259+
}
260+
224261
/// Map holding trait implementations for types.
225262
///
226263
/// Note: "impl self" blocks are considered traits and are stored in the
@@ -247,6 +284,70 @@ pub(crate) enum IsImplInterfaceSurface {
247284
No,
248285
}
249286

287+
impl DebugWithEngines for TraitMap {
288+
fn fmt(&self, f: &mut fmt::Formatter<'_>, engines: &Engines) -> fmt::Result {
289+
if self.trait_impls.is_empty() {
290+
return write!(f, "TraitMap {{ <empty> }}");
291+
}
292+
293+
writeln!(f, "TraitMap {{")?;
294+
295+
for (root, entries) in &self.trait_impls {
296+
writeln!(f, " [root={:?}]", engines.help_out(root))?;
297+
298+
for se in entries {
299+
let entry = &se.inner;
300+
let key = &entry.key;
301+
let value = &entry.value;
302+
303+
let trait_name_str = engines.help_out(&*key.name).to_string();
304+
let ty_str = engines.help_out(key.type_id).to_string();
305+
306+
let iface_flag = match key.is_impl_interface_surface {
307+
IsImplInterfaceSurface::Yes => "interface_surface",
308+
IsImplInterfaceSurface::No => "impl",
309+
};
310+
311+
let mut impl_tparams = String::new();
312+
if !key.impl_type_parameters.is_empty() {
313+
impl_tparams.push_str(" where [");
314+
let mut first = true;
315+
for t in &key.impl_type_parameters {
316+
if !first {
317+
impl_tparams.push_str(", ");
318+
}
319+
first = false;
320+
impl_tparams.push_str(&engines.help_out(*t).to_string());
321+
}
322+
impl_tparams.push(']');
323+
}
324+
325+
writeln!(
326+
f,
327+
" impl {} for {} [{}]{} {{",
328+
trait_name_str, ty_str, iface_flag, impl_tparams
329+
)?;
330+
331+
for (name, item) in &value.trait_items {
332+
match item {
333+
ResolvedTraitImplItem::Parsed(_p) => {
334+
writeln!(f, " - {}: <parsed>", name)?;
335+
}
336+
ResolvedTraitImplItem::Typed(ty_item) => {
337+
writeln!(f, " - {}: {:?}", name, engines.help_out(ty_item))?;
338+
}
339+
}
340+
}
341+
342+
writeln!(f, " [impl span: {:?}]", value.impl_span)?;
343+
writeln!(f, " }}")?;
344+
}
345+
}
346+
347+
write!(f, "}}")
348+
}
349+
}
350+
250351
impl TraitMap {
251352
/// Given a [TraitName] `trait_name`, [TypeId] `type_id`, and list of
252353
/// [TyImplItem](ty::TyImplItem) `items`, inserts
@@ -713,6 +814,62 @@ impl TraitMap {
713814
self.filter_by_type_inner(engines, all_types, decider2),
714815
engines,
715816
);
817+
818+
// include indirect trait impls for cases like StorageKey<T>
819+
for key in self.trait_impls.keys() {
820+
for entry in self.trait_impls[key].iter() {
821+
let TraitEntry {
822+
key:
823+
TraitKey {
824+
name: trait_name,
825+
type_id: trait_type_id,
826+
trait_decl_span,
827+
impl_type_parameters,
828+
is_impl_interface_surface,
829+
},
830+
value:
831+
TraitValue {
832+
trait_items,
833+
impl_span,
834+
},
835+
} = entry.inner.as_ref();
836+
837+
let decider3 =
838+
|left: TypeId, right: TypeId| unify_checker_for_item_import.check(right, left);
839+
840+
let matches_generic_params = match *engines.te().get(*trait_type_id) {
841+
TypeInfo::Enum(decl_id) => engines
842+
.de()
843+
.get(&decl_id)
844+
.generic_parameters
845+
.iter()
846+
.any(|gp| gp.unifies(type_id, decider3)),
847+
TypeInfo::Struct(decl_id) => engines
848+
.de()
849+
.get(&decl_id)
850+
.generic_parameters
851+
.iter()
852+
.any(|gp| gp.unifies(type_id, decider3)),
853+
_ => false,
854+
};
855+
856+
if matches_generic_params
857+
|| impl_type_parameters.iter().any(|tp| decider(type_id, *tp))
858+
{
859+
trait_map.insert_inner(
860+
trait_name.clone(),
861+
impl_span.clone(),
862+
trait_decl_span.clone(),
863+
*trait_type_id,
864+
impl_type_parameters.clone(),
865+
trait_items.clone(),
866+
is_impl_interface_surface.clone(),
867+
engines,
868+
);
869+
}
870+
}
871+
}
872+
716873
trait_map
717874
}
718875

sway-core/src/type_system/ast_elements/type_parameter.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ impl TypeParameter {
165165

166166
Ok(())
167167
}
168+
169+
pub(crate) fn unifies(
170+
&self,
171+
type_id: TypeId,
172+
decider: impl Fn(TypeId, TypeId) -> bool,
173+
) -> bool {
174+
match self {
175+
TypeParameter::Type(generic_type_parameter) => {
176+
decider(type_id, generic_type_parameter.type_id)
177+
}
178+
TypeParameter::Const(const_generic_parameter) => {
179+
decider(type_id, const_generic_parameter.ty)
180+
}
181+
}
182+
}
168183
}
169184

170185
impl Named for TypeParameter {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[[package]]
2+
name = "std"
3+
source = "path+from-root-4F829CC0A9DE5C47"
4+
5+
[[package]]
6+
name = "trait_map_use_indirect_impl"
7+
source = "member"
8+
dependencies = ["std"]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[project]
2+
name = "trait_map_use_indirect_impl"
3+
authors = ["Fuel Labs <[email protected]>"]
4+
entry = "main.sw"
5+
license = "Apache-2.0"
6+
7+
[dependencies]
8+
std = { path = "../../../../../../sway-lib-std" }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
library;
2+
3+
pub struct StorageFoobar {}
4+
5+
impl StorageKey<StorageFoobar> {
6+
pub fn foobar(self) {
7+
log("foobar");
8+
}
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
contract;
2+
3+
mod foobar;
4+
5+
use foobar::StorageFoobar;
6+
7+
storage {
8+
foo: StorageFoobar = StorageFoobar {},
9+
}
10+
11+
impl Contract {
12+
fn foobar() {
13+
storage.foo.foobar();
14+
}
15+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
category = "compile"
2+
expected_warnings = 0

0 commit comments

Comments
 (0)