Generic aggregate bound checking ignores trait type arguments
Generic aggregate bound checking ignores trait type arguments
Related Component
compiler
Problem
When checking whether an unknown generic satisfies the bounds of a nested aggregate, TypeId::check_type_parameter_bounds() compares only the trait call path and ignores the trait's type arguments. As a result, a function constrained by T: Mark<u8> can construct and return a type whose declaration requires T: Mark<u64>.
The equivalent direct concrete construction is correctly rejected, so this is a generic-boundary bypass rather than an accepted implementation of the required trait.
Steps
This script compiles and its test passes on v0.72.1:
script;
trait Mark<A> {}
struct NeedsU64<T> where T: Mark<u64> {
value: T,
}
struct OnlyU8 { n: u64 }
impl Mark<u8> for OnlyU8 {}
fn forge<T>(x: T) -> NeedsU64<T> where T: Mark<u8> {
NeedsU64 { value: x }
}
fn main() -> u64 {
let impossible: NeedsU64<OnlyU8> = forge(OnlyU8 { n: 42 });
impossible.value.n
}
#[test]
fn invalidly_bounded_type_is_inhabited() {
assert(main() == 42);
}
Run:
forc test
Observed: 1 passed; 0 failed.
Control: replacing forge(...) with the direct construction below is rejected:
let impossible: NeedsU64<OnlyU8> = NeedsU64 {
value: OnlyU8 { n: 42 },
};
The compiler then correctly reports that Mark<u64> is not implemented for OnlyU8.
In sway-core/src/type_system/id.rs, the TypeInfo::UnknownGeneric branch of check_type_parameter_bounds() collects only trait_constraint.trait_name, then uses name-only containment. The concrete branch below it already compares canonical names, arity, and resolved trait arguments.
Expected behavior
forge must be rejected because Mark<u8> does not satisfy Mark<u64>. Trait constraints with the same path but different type arguments are distinct requirements.
Possible Solution(s)
Compare complete substituted trait constraints in the unknown-generic branch: canonical path, arity, and recursively unified/resolved generic arguments. The concrete branch's constraint comparison may be reusable.
Add negative tests for identical trait paths with different type arguments, including nested aggregate return types and constructors.
This is not the same defect as closed #6374, which involved calling a method whose additional method-level constraint was not respected. Here the aggregate's declared bound is checked, but its trait arguments are discarded.
Installed components
forc 0.72.1
sway 0.72.1
commit dad95cc42b0383b4e3bacdeda9766565aa584a92
Source: FuelLabs/sway