# How Trait Coherence Checking Works in Sway's Type System: Orphan and Overlap Rules Explained

> Understand trait coherence checking in Sway. Learn how orphan and overlap rules ensure unambiguous trait implementations across packages for deterministic code.

- Repository: [Fuel Labs/sway](https://github.com/FuelLabs/sway)
- Tags: deep-dive
- Published: 2026-03-04

---

**Trait coherence checking in Sway enforces the orphan rule and overlap rule during semantic analysis to guarantee that trait implementations are unambiguous and deterministic across packages.**

Sway's type system guarantees **trait coherence** through a rigorous two-phase validation process that runs during semantic analysis in the `FuelLabs/sway` compiler. This mechanism ensures that no two implementations of a trait can conflict for the same type, and that implementations respect package boundaries to prevent downstream crates from hijacking foreign traits. The checks are orchestrated through [`sway-core/src/semantic_analysis/namespace/trait_coherence.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/semantic_analysis/namespace/trait_coherence.rs) and [`trait_map.rs`](https://github.com/FuelLabs/sway/blob/main/trait_map.rs), leveraging the compiler's `Engines` and `Handler` infrastructure.

## The Two Phases of Trait Coherence Checking in Sway

Sway validates trait implementations in two distinct phases that operate on the `TraitMap` after all module items have been collected.

### Phase 1: Orphan Rule Validation

The **orphan rule** prevents implementations where neither the trait nor any of the types being implemented are defined in the current package. This stops downstream crates from writing `impl ForeignTrait for ForeignType`, which would create ambiguity if multiple crates attempted the same implementation.

### Phase 2: Overlap Detection

The **overlap rule** guarantees that for any concrete type-trait pair, at most one implementation applies. This is enforced by attempting to unify the type signatures of every pair of implementations; if two heads can unify and their trait bounds are satisfied, the compiler rejects the program.

## Orphan Rule Implementation in [`trait_coherence.rs`](https://github.com/FuelLabs/sway/blob/main/trait_coherence.rs)

The orphan check is driven by `check_orphan_rules_for_impls`, which traverses every lexical scope in the root module and delegates to `check_orphan_rules_for_impls_in_scope`.

```rust
// sway-core/src/semantic_analysis/namespace/trait_coherence.rs
pub(crate) fn check_orphan_rules_for_impls(
    handler: &Handler,
    engines: &Engines,
    current_package: &Package,
) -> Result<(), ErrorEmitted> {
    module.walk_scope_chain(|lexical_scope| {
        let trait_map = &lexical_scope.items.implemented_traits;
        check_orphan_rules_for_impls_in_scope(handler, engines, current_package, trait_map)
    });
}

```

Inside `check_orphan_rules_for_impls_in_scope`, the compiler performs four critical steps:

1. **Skips contract types**—they are exempt from coherence checking.
2. **Filters by package**—impls originating from external packages are ignored.
3. **Checks trait origin**—if the trait is defined in the current package, the impl is automatically valid.
4. **Scans for local types**—via `references_local_type`, the compiler walks the impl's type arguments, generic parameters, and self-type to detect any reference to a local enum, struct, or array.

The `references_local_type` function uses `type_id.walk_inner_types` to recursively inspect types:

```rust
fn references_local_type(
    engines: &Engines,
    current_package: &Package,
    type_id: TypeId,
) -> bool {
    let found_local = Cell::new(false);
    type_id.walk_inner_types(
        engines,
        IncludeSelf::Yes,
        &|inner_type_id| {
            let inner_type = engines.te().get(*inner_type_id);
            let is_local = match *inner_type {
                TypeInfo::Enum(decl_id) => {
                    let enum_decl = engines.de().get_enum(&decl_id);
                    is_from_local_package(current_package, &enum_decl.call_path)
                }
                TypeInfo::Struct(decl_id) => {
                    let struct_decl = engines.de().get_struct(&decl_id);
                    is_from_local_package(current_package, &struct_decl.call_path)
                }
                TypeInfo::Array(_, _) | TypeInfo::StringArray(_) => true,
                _ => false,
            };
            if is_local { found_local.set(true); }
        },
        &|trait_constraint| {
            if is_from_local_package(current_package, &trait_constraint.trait_name) {
                found_local.set(true);
            }
        },
    );
    found_local.get()
}

```

If no local type is found, the compiler emits `CompileError::IncoherentImplDueToOrphanRule`.

## Overlap Checking in [`trait_map.rs`](https://github.com/FuelLabs/sway/blob/main/trait_map.rs)

Once orphan rules pass, the compiler validates that no two implementations overlap using `check_impls_for_overlap` in [`sway-core/src/semantic_analysis/namespace/trait_map.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/semantic_analysis/namespace/trait_map.rs).

The algorithm builds a map of trait-to-concrete-types for fast lookup, then compares every pair of implementations:

1. **Unification test**: Uses `UnifyCheck::constraint_subset` to determine if the two impl heads can be made identical through type substitution.
2. **Reference handling**: Custom `is_unified_type_subset` logic ensures that `&T` does not unify with `&mut T` for impl selection.
3. **Constraint validation**: Collects trait constraints (`self_tcs` and `other_tcs`) from generic parameters and verifies they are satisfied by the concrete types in the trait map.

If two impls unify and all constraints are satisfied, the compiler reports:

```rust
handler.emit_err(CompileError::ConflictingImplsForTraitAndType {
    trait_name: engines.help_out(self_entry.inner.key.name.as_ref()).to_string(),
    type_implementing_for: engines.help_out(self_entry.inner.key.type_id).to_string(),
    type_implementing_for_unaliased: engines.help_out(self_entry.inner.key.type_id).to_string(),
    existing_impl_span: self_entry.inner.value.impl_span.clone(),
    second_impl_span: other_entry.span.clone(),
});

```

If no overlap is detected, the trait maps are merged via `trait_map.extend(other, engines)`.

## Code Examples: Valid and Invalid Impls

### ✅ Valid Coherent Implementation

This implementation passes both orphan and overlap checks because the trait and the type are both defined in the current package.

```sway
library;

// Trait defined in this package.
trait Foo<T> {
    fn foo(self, x: T) -> T;
}

// Local struct – the impl references a local type.
struct Bar<T> {
    inner: T,
}

// Implementation is permitted because the trait is local *and* the self‑type
// (`Bar<T>`) is also local.
impl<T> Foo<T> for Bar<T> {
    fn foo(self, x: T) -> T {
        x
    }
}

```

### ❌ Orphan Rule Violation

This implementation fails because it implements a foreign trait for a foreign type without referencing any local types.

```sway
// External trait `External::Baz` lives in a different package.
use external::Baz;

// No local type appears in the impl; only a generic parameter.
impl<T> Baz<T> for T {
    fn baz(self) -> T { self }
}

```

**Compiler output:**

```

error: incoherent impl due to orphan rule
  --> src/main.sw:8:1
   |
8  | impl<T> Baz<T> for T {
   | ^^^^^^^^^^^^^^^^^^^^^
   |
   = note: the trait `external::Baz` is defined in crate `external`
   = note: the type `T` is not a local type

```

### ❌ Overlap Rule Violation

These implementations conflict because the generic `impl<T> MyTrait for T` overlaps with the concrete implementations for `S` and `T`.

```sway
trait MyTrait {}
struct S;
struct T;

// Both impls are valid individually.
impl MyTrait for S {}
impl MyTrait for T {}

// A generic impl that overlaps with the concrete ones.
impl<T> MyTrait for T {}

```

**Compiler output:**

```

error: conflicting implementations of trait `MyTrait` for type `T`
  --> src/main.sw:9:1
   |
5  | impl MyTrait for S {}
   | ------------------- first impl
9  | impl<T> MyTrait for T {}
   | ^^^^^^^^^^^^^^^^^^^^ second impl
   = note: the impls overlap because `T` can be instantiated as `S` or `T`

```

## Summary

- **Two-phase validation**: Sway checks trait coherence through orphan-rule validation followed by overlap detection during semantic analysis.
- **Orphan rule enforcement**: The `check_orphan_rules_for_impls` function in [`trait_coherence.rs`](https://github.com/FuelLabs/sway/blob/main/trait_coherence.rs) ensures every implementation references either a local trait or a local type, preventing downstream crates from hijacking foreign trait-type pairs.
- **Overlap prevention**: The `check_impls_for_overlap` function in [`trait_map.rs`](https://github.com/FuelLabs/sway/blob/main/trait_map.rs) uses unification (`UnifyCheck::constraint_subset`) to verify that no two implementations can apply to the same concrete type, ensuring deterministic method resolution.
- **Error specificity**: Violations emit precise compiler errors (`IncoherentImplDueToOrphanRule` or `ConflictingImplsForTraitAndType`) with source spans to guide developers toward coherent designs.

## Frequently Asked Questions

### What is the orphan rule in Sway's type system?

The **orphan rule** is a coherence constraint that prevents implementations where neither the trait nor any of the types involved are defined in the current package. According to the implementation in [`trait_coherence.rs`](https://github.com/FuelLabs/sway/blob/main/trait_coherence.rs), an implementation passes the orphan check only if the trait is defined locally **or** the implementation references at least one local type (enum, struct, or array). This prevents downstream crates from writing `impl ForeignTrait for ForeignType`, which would create ambiguity if multiple crates attempted the same implementation.

### How does Sway detect overlapping trait implementations?

Sway detects overlapping implementations through the `check_impls_for_overlap` function in [`trait_map.rs`](https://github.com/FuelLabs/sway/blob/main/trait_map.rs). The compiler builds a map of trait-to-concrete-types and compares every pair of implementations using **unification** (`UnifyCheck::constraint_subset`). If two implementation heads can be unified (made identical through type substitution) and their trait bounds are satisfied, the compiler emits `CompileError::ConflictingImplsForTraitAndType`. This ensures that for any concrete type-trait pair, exactly one implementation applies, enabling deterministic method resolution.

### Why does Sway reject generic implementations like `impl<T> Trait for T`?

Sway rejects blanket implementations like `impl<T> Trait for T` when they overlap with existing concrete implementations (such as `impl Trait for MyStruct`) because this violates the **overlap rule**. During coherence checking, the compiler unifies the generic `T` with concrete types like `MyStruct`, determining that both implementations could apply to the same type. Without rejecting this overlap, method dispatch would be ambiguous. The compiler reports this as `ConflictingImplsForTraitAndType`, requiring developers to either remove the concrete implementations or constrain the generic implementation with trait bounds that exclude the concrete types.

### Where in the Sway compiler does trait coherence checking occur?

Trait coherence checking occurs during **semantic analysis** in the `sway-core` crate, specifically within the namespace resolution phase. The orphan rule check is implemented in [`sway-core/src/semantic_analysis/namespace/trait_coherence.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/semantic_analysis/namespace/trait_coherence.rs) via `check_orphan_rules_for_impls`, while the overlap check resides in [`sway-core/src/semantic_analysis/namespace/trait_map.rs`](https://github.com/FuelLabs/sway/blob/main/sway-core/src/semantic_analysis/namespace/trait_map.rs) within `check_impls_for_overlap`. Both checks are invoked after the module's items have been collected into a `TraitMap`, ensuring that all implementations are validated before method selection occurs.