Repository: starkware-libs/cairo (language: rust)

Issue:
bug: default impl trait bound on associated type not found
# Bug Report

**Cairo version:**
6837d541e5702f9098ea0797cf806114fa097008
<!-- Please specify commit or tag version. -->

**Current behavior:**
```rust
    fn next(ref self: T) -> Option<Self::Item>;

    fn advance_by<+Destruct<T>, +Destruct<Self::Item>>(
        ref self: T, n: usize,
    ) -> Result<
        (), NonZero<usize>,
    > {
        let mut n = n;
        let mut res = Result::Ok(());
        while let Option::Some(nz_n) = n.try_into() {
            if let Option::Some(_) = Self::next(ref self) {
                n -= 1;
            } else {
                res = Result::Err(nz_n);
                break;
            }
        };
        res
    }
}

```

Implementing this should work in theory, but in practice, the following test fails to compile because:

```rust
#[test]
fn test_advance_by() {
    let mut iter = array![1_u8, 2, 3, 4].into_iter();

    assert_eq!(iter.advance_by(2), Result::Ok(()));
    assert_eq!(iter.next(), Option::Some(3));
    assert_eq!(iter.advance_by(0), Result::Ok(()));
    assert_eq!(iter.advance_by(100), Result::Err(99));
}
```

```
error: Trait has no implementation in context: core::traits::Destruct::<Iterator::Item>.
 --> corelib/src/test/iter_test.cairo:5:21
    assert_eq!(iter.advance_by(2), Result::Ok(()));
```


Discussion / hints:

Fix patch:
diff --git a/crates/cairo-lang-semantic/src/expr/inference/infers.rs b/crates/cairo-lang-semantic/src/expr/inference/infers.rs
index 6d98f4b118c..382649d97bd 100644
--- a/crates/cairo-lang-semantic/src/expr/inference/infers.rs
+++ b/crates/cairo-lang-semantic/src/expr/inference/infers.rs
@@ -92,7 +92,7 @@ pub trait InferenceEmbeddings {
         concrete_trait_function: ConcreteTraitGenericFunctionId,
         lookup_context: &ImplLookupContext,
         stable_ptr: Option<SyntaxStablePtrId>,
-    ) -> GenericFunctionId;
+    ) -> ImplGenericFunctionId;
     fn infer_trait_type(
         &mut self,
         concrete_trait_type: ConcreteTraitTypeId,
@@ -402,8 +402,11 @@ impl InferenceEmbeddings for Inference<'_> {
         lookup_context: &ImplLookupContext,
         stable_ptr: Option<SyntaxStablePtrId>,
     ) -> InferenceResult<FunctionId> {
-        let generic_function =
-            self.infer_trait_generic_function(concrete_trait_function, lookup_context, stable_ptr);
+        let generic_function = GenericFunctionId::Impl(self.infer_trait_generic_function(
+            concrete_trait_function,
+            lookup_context,
+            stable_ptr,
+        ));
         self.infer_generic_function(generic_function, lookup_context, stable_ptr)
     }
 
@@ -430,16 +433,13 @@ impl InferenceEmbeddings for Inference<'_> {
         concrete_trait_function: ConcreteTraitGenericFunctionId,
         lookup_context: &ImplLookupContext,
         stable_ptr: Option<SyntaxStablePtrId>,
-    ) -> GenericFunctionId {
+    ) -> ImplGenericFunctionId {
         let impl_id = self.new_impl_var(
             concrete_trait_function.concrete_trait(self.db),
             stable_ptr,
             lookup_context.clone(),
         );
-        GenericFunctionId::Impl(ImplGenericFunctionId {
-            impl_id,
-            function: concrete_trait_function.trait_function(self.db),
-        })
+        ImplGenericFunctionId { impl_id, function: concrete_trait_function.trait_function(self.db) }
     }
 
     /// Infers the impl to be substituted instead of a trait for a given trait type.
diff --git a/crates/cairo-lang-semantic/src/items/imp.rs b/crates/cairo-lang-semantic/src/items/imp.rs
index ef9b9578f37..d6f840b2b83 100644
--- a/crates/cairo-lang-semantic/src/items/imp.rs
+++ b/crates/cairo-lang-semantic/src/items/imp.rs
@@ -1942,12 +1942,6 @@ pub fn infer_impl_by_self(
             .intern(ctx.db);
     let trait_func_generic_params =
         ctx.db.concrete_trait_function_generic_params(concrete_trait_function_id).unwrap();
-    let generic_args = ctx.resolver.resolve_generic_args(
-        ctx.diagnostics,
-        &trait_func_generic_params,
-        &generic_args_syntax.unwrap_or_default(),
-        stable_ptr,
-    )?;
 
     let impl_lookup_context = ctx.resolver.impl_lookup_context();
     let inference = &mut ctx.resolver.inference();
@@ -1956,10 +1950,22 @@ pub fn infer_impl_by_self(
         &impl_lookup_context,
         Some(stable_ptr),
     );
+    let generic_args = ctx.resolver.resolve_generic_args(
+        ctx.diagnostics,
+        GenericSubstitution::from_impl(generic_function.impl_id),
+        &trait_func_generic_params,
+        &generic_args_syntax.unwrap_or_default(),
+        stable_ptr,
+    )?;
 
     Ok((
-        FunctionLongId { function: ConcreteFunction { generic_function, generic_args } }
-            .intern(ctx.db),
+        FunctionLongId {
+            function: ConcreteFunction {
+                generic_function: GenericFunctionId::Impl(generic_function),
+                generic_args,
+            },
+        }
+        .intern(ctx.db),
         n_snapshots,
     ))
 }
diff --git a/crates/cairo-lang-semantic/src/resolve/mod.rs b/crates/cairo-lang-semantic/src/resolve/mod.rs
index cf3eee0d224..ba595725ece 100644
--- a/crates/cairo-lang-semantic/src/resolve/mod.rs
+++ b/crates/cairo-lang-semantic/src/resolve/mod.rs
@@ -869,11 +869,12 @@ impl<'db> Resolver<'db> {
                             }
                         }
                         let impl_lookup_context = self.impl_lookup_context();
-                        let generic_function = self.inference().infer_trait_generic_function(
-                            concrete_trait_function,
-                            &impl_lookup_context,
-                            Some(identifier_stable_ptr),
-                        );
+                        let generic_function =
+                            GenericFunctionId::Impl(self.inference().infer_trait_generic_function(
+                                concrete_trait_function,
+                                &impl_lookup_context,
+                                Some(identifier_stable_ptr),
+                            ));
 
                         Ok(ResolvedConcreteItem::Function(self.specialize_function(
                             diagnostics,
@@ -1076,6 +1077,7 @@ impl<'db> Resolver<'db> {
                     self.db.module_type_alias_generic_params(module_type_alias_id)?;
                 let generic_args = self.resolve_generic_args(
                     diagnostics,
+                    GenericSubstitution::default(),
                     &generic_params,
                     &generic_args_syntax.unwrap_or_default(),
                     identifier.stable_ptr().untyped(),
@@ -1090,6 +1092,7 @@ impl<'db> Resolver<'db> {
                 let generic_params = self.db.impl_alias_generic_params(impl_alias_id)?;
                 let generic_args = self.resolve_generic_args(
                     diagnostics,
+                    GenericSubstitution::default(),
                     &generic_params,
                     &generic_args_syntax.unwrap_or_default(),
                     identifier.stable_ptr().untyped(),
@@ -1364,8 +1367,13 @@ impl<'db> Resolver<'db> {
             .db
             .trait_generic_params(trait_id)
             .map_err(|_| diagnostics.report(stable_ptr, UnknownTrait))?;
-        let generic_args =
-            self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?;
+        let generic_args = self.resolve_generic_args(
+            diagnostics,
+            GenericSubstitution::default(),
+            &generic_params,
+            generic_args,
+            stable_ptr,
+        )?;
 
         Ok(ConcreteTraitLongId { trait_id, generic_args }.intern(self.db))
     }
@@ -1383,8 +1391,13 @@ impl<'db> Resolver<'db> {
             .db
             .impl_def_generic_params(impl_def_id)
             .map_err(|_| diagnostics.report(stable_ptr, UnknownImpl))?;
-        let generic_args =
-            self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?;
+        let generic_args = self.resolve_generic_args(
+            diagnostics,
+            GenericSubstitution::default(),
+            &generic_params,
+            generic_args,
+            stable_ptr,
+        )?;
 
         Ok(ConcreteImplLongId { impl_def_id, generic_args }.intern(self.db))
     }
@@ -1401,6 +1414,7 @@ impl<'db> Resolver<'db> {
             enum_id: variant_id.enum_id(self.db.upcast()),
             generic_args: self.resolve_generic_args(
                 diagnostics,
+                GenericSubstitution::default(),
                 &self.db.enum_generic_params(variant_id.enum_id(self.db.upcast()))?,
                 generic_args,
                 stable_ptr,
@@ -1423,8 +1437,18 @@ impl<'db> Resolver<'db> {
     ) -> Maybe<FunctionId> {
         // TODO(lior): Should we report diagnostic if `impl_def_generic_params` failed?
         let generic_params: Vec<_> = generic_function.generic_params(self.db)?;
-        let generic_args =
-            self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?;
+        let substitution = if let GenericFunctionId::Impl(id) = generic_function {
+            GenericSubstitution::from_impl(id.impl_id)
+        } else {
+            GenericSubstitution::default()
+        };
+        let generic_args = self.resolve_generic_args(
+            diagnostics,
+            substitution,
+            &generic_params,
+            generic_args,
+            stable_ptr,
+        )?;
 
         Ok(FunctionLongId { function: ConcreteFunction { generic_function, generic_args } }
             .intern(self.db))
@@ -1442,8 +1466,13 @@ impl<'db> Resolver<'db> {
             .db
             .generic_type_generic_params(generic_type)
             .map_err(|_| diagnostics.report(stable_ptr, UnknownType))?;
-        let generic_args =
-            self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?;
+        let generic_args = self.resolve_generic_args(
+            diagnostics,
+            GenericSubstitution::default(),
+            &generic_params,
+            generic_args,
+            stable_ptr,
+        )?;
 
         Ok(TypeLongId::Concrete(ConcreteTypeId::new(self.db, generic_type, generic_args))
             .intern(self.db))
@@ -1471,11 +1500,11 @@ impl<'db> Resolver<'db> {
     pub fn resolve_generic_args(
         &mut self,
         diagnostics: &mut SemanticDiagnostics,
+        mut substitution: GenericSubstitution,
         generic_params: &[GenericParam],
         generic_args_syntax: &[ast::GenericArg],
         stable_ptr: SyntaxStablePtrId,
     ) -> Maybe<Vec<GenericArgumentId>> {
-        let mut substitution = GenericSubstitution::default();
         let mut resolved_args = vec![];
         let arg_syntax_per_param =
             self.get_arg_syntax_per_param(diagnostics, generic_params, generic_args_syntax)?;
@@ -1901,6 +1930,7 @@ impl<'db> Resolver<'db> {
         }
         let resolved_args = self.resolve_generic_args(
             diagnostics,
+            GenericSubstitution::default(),
             &generic_params,
             current_segment_generic_args,
             segment_stable_ptr,


Test patch:
diff --git a/tests/bug_samples/issue7060.cairo b/tests/bug_samples/issue7060.cairo
new file mode 100644
index 00000000000..fd52874b7da
--- /dev/null
+++ b/tests/bug_samples/issue7060.cairo
@@ -0,0 +1,16 @@
+pub trait WithAssociated<T> {
+    type S;
+    fn foo<+Drop<T>, +Drop<Self::S>>(self: T, s: Self::S) -> Self::S {
+        s
+    }
+}
+
+impl Impl<T> of WithAssociated<T> {
+    type S = u8;
+}
+
+#[test]
+fn test_associated_type_usage() {
+    WithAssociated::foo(4, 5_u8);
+    4.foo(5_u8);
+}
diff --git a/tests/bug_samples/lib.cairo b/tests/bug_samples/lib.cairo
index 42ca1448fb6..119677c4d5f 100644
--- a/tests/bug_samples/lib.cairo
+++ b/tests/bug_samples/lib.cairo
@@ -55,6 +55,7 @@ mod issue6920;
 mod issue6968;
 mod issue7031;
 mod issue7038;
+mod issue7060;
 mod loop_break_in_match;
 mod loop_only_change;
 mod partial_param_local;