All Journeys
Journey #11 Complex

I am a derived trait

Derived Eq trait on structs and sum types with field and tag-based equality

10
Score
PASS Status
33 Expected
PASS Overflow

What you'll learn

  • See how #derive(Eq) generates per-type comparison functions in LLVM IR
  • Understand tag-based dispatch for sum type equality
  • Compare ideal vs actual codegen for struct operations
  • Evaluate the short-circuit pattern in derived equality methods

Score Breakdown

derived traitstrait methodsstruct constructionsum typespattern matching

Journey 11: “I am a derived trait”

Source

// Journey 11: "I am a derived trait"
// Slug: derived-traits
// Difficulty: complex
// Features: derived_traits, trait_methods, struct_construction, sum_types, pattern_matching
// Expected: check_struct_eq() + check_sum_eq() + check_nested() = 7 + 11 + 15 = 33

#[derive(Eq)]
type Point = { x: int, y: int }

#[derive(Eq)]
type Color = Red | Green | Blue;

#[derive(Eq)]
type Shape = Circle(radius: int) | Rect(w: int, h: int);

@check_struct_eq () -> int = {
    let p1 = Point { x: 10, y: 20 };
    let p2 = Point { x: 10, y: 20 };
    let p3 = Point { x: 10, y: 30 };
    let same = if p1 == p2 then 3 else 0;
    let diff = if p1 != p3 then 4 else 0;
    same + diff
    // = 3 + 4 = 7
}

@check_sum_eq () -> int = {
    let c1 = Red;
    let c2 = Red;
    let c3 = Blue;
    let unit_same = if c1 == c2 then 5 else 0;
    let unit_diff = if c1 != c3 then 6 else 0;
    unit_same + unit_diff
    // = 5 + 6 = 11
}

@check_nested () -> int = {
    let s1 = Circle(radius: 10);
    let s2 = Circle(radius: 10);
    let s3 = Rect(w: 5, h: 8);
    let payload_same = if s1 == s2 then 7 else 0;
    let payload_diff = if s1 != s3 then 8 else 0;
    payload_same + payload_diff
    // = 7 + 8 = 15
}

@main () -> int = {
    let a = check_struct_eq();   // = 7
    let b = check_sum_eq();      // = 11
    let c = check_nested();      // = 15
    a + b + c                    // = 33
}

Execution Results

BackendExit CodeExpectedStdoutStderrStatus
Eval3333(none)(none)PASS
AOT3333(none)(none)PASS

Compiler Pipeline

1. Lexer

The lexer (tokenizer) breaks raw source text into a stream of tokens — the smallest meaningful units like keywords, identifiers, operators, and literals.

Tokens: 346 | Keywords: ~20 | Identifiers: ~45 | Errors: 0

Token stream (excerpt)
Hash Lbracket Ident(derive) LParen Ident(Eq) RParen Rbracket
Ident(type) Ident(Point) Eq LBrace Ident(x) Colon Ident(int) Comma
Ident(y) Colon Ident(int) RBrace
Hash Lbracket Ident(derive) LParen Ident(Eq) RParen Rbracket
Ident(type) Ident(Color) Eq Ident(Red) Pipe Ident(Green) Pipe Ident(Blue) Semi
Hash Lbracket Ident(derive) LParen Ident(Eq) RParen Rbracket
Ident(type) Ident(Shape) Eq Ident(Circle) LParen Ident(radius) Colon Ident(int) RParen
  Pipe Ident(Rect) LParen Ident(w) Colon Ident(int) Comma Ident(h) Colon Ident(int) RParen Semi
Fn(@) Ident(check_struct_eq) LParen RParen Arrow Ident(int) Eq LBrace ...

2. Parser

The parser transforms the flat token stream into a hierarchical Abstract Syntax Tree (AST) — a tree structure that represents the grammatical structure of the program.

Nodes: 85 | Max depth: 5 | Functions: 4 | Types: 3 | Errors: 0

AST (simplified)
Module
├─ TypeDecl Point #derive(Eq)
│  └─ Struct { x: int, y: int }
├─ TypeDecl Color #derive(Eq)
│  └─ Sum: Red | Green | Blue
├─ TypeDecl Shape #derive(Eq)
│  └─ Sum: Circle(radius: int) | Rect(w: int, h: int)
├─ FnDecl @check_struct_eq
│  ├─ Return: int
│  └─ Body: Block
│       ├─ Let p1 = Point { x: 10, y: 20 }
│       ├─ Let p2 = Point { x: 10, y: 20 }
│       ├─ Let p3 = Point { x: 10, y: 30 }
│       ├─ Let same = If(p1 == p2, 3, 0)
│       ├─ Let diff = If(p1 != p3, 4, 0)
│       └─ BinOp(+): same + diff
├─ FnDecl @check_sum_eq
│  ├─ Return: int
│  └─ Body: Block
│       ├─ Let c1 = Red
│       ├─ Let c2 = Red
│       ├─ Let c3 = Blue
│       ├─ Let unit_same = If(c1 == c2, 5, 0)
│       ├─ Let unit_diff = If(c1 != c3, 6, 0)
│       └─ BinOp(+): unit_same + unit_diff
├─ FnDecl @check_nested
│  ├─ Return: int
│  └─ Body: Block
│       ├─ Let s1 = Circle(radius: 10)
│       ├─ Let s2 = Circle(radius: 10)
│       ├─ Let s3 = Rect(w: 5, h: 8)
│       ├─ Let payload_same = If(s1 == s2, 7, 0)
│       ├─ Let payload_diff = If(s1 != s3, 8, 0)
│       └─ BinOp(+): payload_same + payload_diff
└─ FnDecl @main
   ├─ Return: int
   └─ Body: Block
        ├─ Let a = Call(@check_struct_eq)
        ├─ Let b = Call(@check_sum_eq)
        ├─ Let c = Call(@check_nested)
        └─ BinOp(+): BinOp(+): a + b + c

3. Type Checker

The type checker verifies that all expressions have compatible types using Hindley-Milner type inference. It resolves type variables, checks constraints, and ensures type safety. For derived traits, it registers synthesized method signatures (e.g., Point.equals(self, other: Point) -> bool).

Constraints: ~35 | Types inferred: ~20 | Unifications: ~30 | Errors: 0

Inferred types
// #derive(Eq) generates:
// Point.equals: (self: Point, other: Point) -> bool
// Color.equals: (self: Color, other: Color) -> bool
// Shape.equals: (self: Shape, other: Shape) -> bool

@check_struct_eq () -> int = {
    let p1 = Point { x: 10, y: 20 };   // : Point
    let p2 = Point { x: 10, y: 20 };   // : Point
    let p3 = Point { x: 10, y: 30 };   // : Point
    let same = if p1 == p2 then 3 else 0;  // == desugars to Point.equals -> bool
    let diff = if p1 != p3 then 4 else 0;  // != desugars to !Point.equals -> bool
    same + diff  // : int
}

@check_sum_eq () -> int = {
    let c1 = Red;   // : Color (tag=0)
    let c2 = Red;   // : Color (tag=0)
    let c3 = Blue;  // : Color (tag=2)
    let unit_same = if c1 == c2 then 5 else 0;  // Color.equals
    let unit_diff = if c1 != c3 then 6 else 0;  // !Color.equals
    unit_same + unit_diff  // : int
}

@check_nested () -> int = {
    let s1 = Circle(radius: 10);  // : Shape (tag=0, payload=[10, 0])
    let s2 = Circle(radius: 10);  // : Shape (tag=0, payload=[10, 0])
    let s3 = Rect(w: 5, h: 8);   // : Shape (tag=1, payload=[5, 8])
    let payload_same = if s1 == s2 then 7 else 0;  // Shape.equals
    let payload_diff = if s1 != s3 then 8 else 0;  // !Shape.equals
    payload_same + payload_diff  // : int
}

@main () -> int = {
    let a = check_struct_eq();   // : int (= 7)
    let b = check_sum_eq();      // : int (= 11)
    let c = check_nested();      // : int (= 15)
    a + b + c                    // : int (= 33)
}

4. Canonicalization

The canonicalizer transforms the typed AST into a simplified canonical form. For derived traits, == and != are desugared into calls to the generated $eq method, and != becomes !(a == b) (xor with true).

Transforms: 10 | Desugared: 6 | Errors: 0

Key transformations
- #derive(Eq) on Point -> synthesize Point$eq(self, other) -> bool
- #derive(Eq) on Color -> synthesize Color$eq(self, other) -> bool
- #derive(Eq) on Shape -> synthesize Shape$eq(self, other) -> bool
- p1 == p2 -> Point$eq(p1, p2)
- p1 != p3 -> xor(Point$eq(p1, p3), true)
- c1 == c2 -> Color$eq(c1, c2)
- c1 != c3 -> xor(Color$eq(c1, c3), true)
- s1 == s2 -> Shape$eq(s1, s2)
- s1 != s3 -> xor(Shape$eq(s1, s3), true)
- Struct/variant construction lowered to aggregate initializers

5. ARC Pipeline

The ARC (Automatic Reference Counting) pipeline analyzes value lifetimes and inserts reference counting operations. All types in this journey are pure value types (int fields only) — no heap allocations, no RC needed.

RC ops inserted: 0 | Elided: 0 | Net ops: 0

ARC annotations
@check_struct_eq: no heap values -- pure struct/scalar operations
@check_sum_eq: no heap values -- unit variants, tag-only comparison
@check_nested: no heap values -- payload variants, inline fields
@main: no heap values -- pure scalar arithmetic
Point$eq: no heap values -- field comparison only
Color$eq: no heap values -- tag comparison only
Shape$eq: no heap values -- tag + field comparison only

Backend: Interpreter

The interpreter (eval path) executes the canonical IR directly, without compilation. It serves as the reference implementation for correctness testing.

Result: 33 | Status: PASS

Evaluation trace
@main()
  +-- @check_struct_eq()
  |     +-- Point$eq(Point{10,20}, Point{10,20}) -> true
  |     |     same = 3
  |     +-- Point$eq(Point{10,20}, Point{10,30}) -> false
  |     |     diff = 4
  |     +-- 3 + 4 = 7
  +-- @check_sum_eq()
  |     +-- Color$eq(Red, Red) -> true
  |     |     unit_same = 5
  |     +-- Color$eq(Red, Blue) -> false
  |     |     unit_diff = 6
  |     +-- 5 + 6 = 11
  +-- @check_nested()
  |     +-- Shape$eq(Circle(10), Circle(10)) -> true
  |     |     payload_same = 7
  |     +-- Shape$eq(Circle(10), Rect(5,8)) -> false
  |     |     payload_diff = 8
  |     +-- 7 + 8 = 15
  +-- 7 + 11 + 15 = 33
-> 33

Backend: LLVM Codegen

The LLVM backend compiles the canonical IR to LLVM IR, which is then compiled to native machine code via LLVM’s optimization and code generation pipeline. Derived trait methods are compiled as separate functions with specialized codegen for struct vs sum type equality.

ARC Pipeline

RC ops inserted: 0 | Elided: 0 | Net ops: 0

ARC annotations
@check_struct_eq: +0 rc_inc, +0 rc_dec (no heap values)
@check_sum_eq: +0 rc_inc, +0 rc_dec (no heap values)
@check_nested: +0 rc_inc, +0 rc_dec (no heap values)
@main: +0 rc_inc, +0 rc_dec (no heap values)
Point$eq: +0 rc_inc, +0 rc_dec (pure comparison)
Color$eq: +0 rc_inc, +0 rc_dec (pure comparison)
Shape$eq: +0 rc_inc, +0 rc_dec (pure comparison)

Generated LLVM IR

; ModuleID = '11-derived-traits'
source_filename = "11-derived-traits"

%ori.Point = type { i64, i64 }
%ori.Color = type { i64 }
%ori.Shape = type { i64, [2 x i64] }

@ovf.msg = private unnamed_addr constant [29 x i8] c"integer overflow on addition\00", align 1

; Function Attrs: nounwind memory(none) uwtable
; --- @check_struct_eq ---
define fastcc noundef i64 @_ori_check_struct_eq() #0 {
bb0:
  %derived_eq = call fastcc i1 @"_ori_Point$eq"(%ori.Point { i64 10, i64 20 }, %ori.Point { i64 10, i64 20 })
  %sel = select i1 %derived_eq, i64 3, i64 0
  %derived_eq1 = call fastcc i1 @"_ori_Point$eq"(%ori.Point { i64 10, i64 20 }, %ori.Point { i64 10, i64 30 })
  %neq = xor i1 %derived_eq1, true
  %sel2 = select i1 %neq, i64 4, i64 0
  %add = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %sel, i64 %sel2)
  %add.val = extractvalue { i64, i1 } %add, 0
  %add.ovf = extractvalue { i64, i1 } %add, 1
  br i1 %add.ovf, label %add.ovf_panic, label %add.ok

add.ok:
  ret i64 %add.val

add.ovf_panic:
  call void @ori_panic_cstr(ptr @ovf.msg)
  unreachable
}

; Function Attrs: nounwind memory(none) uwtable
; --- @check_sum_eq ---
define fastcc noundef i64 @_ori_check_sum_eq() #0 {
bb0:
  %derived_eq = call fastcc i1 @"_ori_Color$eq"(%ori.Color zeroinitializer, %ori.Color zeroinitializer)
  %sel = select i1 %derived_eq, i64 5, i64 0
  %derived_eq1 = call fastcc i1 @"_ori_Color$eq"(%ori.Color zeroinitializer, %ori.Color { i64 2 })
  %neq = xor i1 %derived_eq1, true
  %sel2 = select i1 %neq, i64 6, i64 0
  %add = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %sel, i64 %sel2)
  %add.val = extractvalue { i64, i1 } %add, 0
  %add.ovf = extractvalue { i64, i1 } %add, 1
  br i1 %add.ovf, label %add.ovf_panic, label %add.ok

add.ok:
  ret i64 %add.val

add.ovf_panic:
  call void @ori_panic_cstr(ptr @ovf.msg)
  unreachable
}

; Function Attrs: nounwind memory(none) uwtable
; --- @check_nested ---
define fastcc noundef i64 @_ori_check_nested() #0 {
bb0:
  %ref_arg3 = alloca %ori.Shape, align 8
  %ref_arg2 = alloca %ori.Shape, align 8
  %ref_arg1 = alloca %ori.Shape, align 8
  %ref_arg = alloca %ori.Shape, align 8
  store %ori.Shape { i64 0, [2 x i64] [i64 10, i64 0] }, ptr %ref_arg, align 8
  store %ori.Shape { i64 0, [2 x i64] [i64 10, i64 0] }, ptr %ref_arg1, align 8
  %derived_eq = call fastcc i1 @"_ori_Shape$eq"(ptr %ref_arg, ptr %ref_arg1)
  %sel = select i1 %derived_eq, i64 7, i64 0
  store %ori.Shape { i64 0, [2 x i64] [i64 10, i64 0] }, ptr %ref_arg2, align 8
  store %ori.Shape { i64 1, [2 x i64] [i64 5, i64 8] }, ptr %ref_arg3, align 8
  %derived_eq4 = call fastcc i1 @"_ori_Shape$eq"(ptr %ref_arg2, ptr %ref_arg3)
  %neq = xor i1 %derived_eq4, true
  %sel5 = select i1 %neq, i64 8, i64 0
  %add = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %sel, i64 %sel5)
  %add.val = extractvalue { i64, i1 } %add, 0
  %add.ovf = extractvalue { i64, i1 } %add, 1
  br i1 %add.ovf, label %add.ovf_panic, label %add.ok

add.ok:
  ret i64 %add.val

add.ovf_panic:
  call void @ori_panic_cstr(ptr @ovf.msg)
  unreachable
}

; Function Attrs: nounwind uwtable
; --- @main ---
define noundef i64 @_ori_main() #1 {
bb0:
  %call = call fastcc i64 @_ori_check_struct_eq()
  %call1 = call fastcc i64 @_ori_check_sum_eq()
  %call2 = call fastcc i64 @_ori_check_nested()
  %add = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %call, i64 %call1)
  %add.val = extractvalue { i64, i1 } %add, 0
  %add.ovf = extractvalue { i64, i1 } %add, 1
  br i1 %add.ovf, label %add.ovf_panic, label %add.ok

add.ok:
  %add3 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %add.val, i64 %call2)
  %add.val4 = extractvalue { i64, i1 } %add3, 0
  %add.ovf5 = extractvalue { i64, i1 } %add3, 1
  br i1 %add.ovf5, label %add.ovf_panic7, label %add.ok6

add.ovf_panic:
  call void @ori_panic_cstr(ptr @ovf.msg)
  unreachable

add.ok6:
  ret i64 %add.val4

add.ovf_panic7:
  call void @ori_panic_cstr(ptr @ovf.msg)
  unreachable
}

; Function Attrs: nounwind uwtable
; --- Point.@eq ---
define fastcc noundef i1 @"_ori_Point$eq"(%ori.Point noundef %0, %ori.Point noundef %1) #1 {
entry:
  %eq.self.x = extractvalue %ori.Point %0, 0
  %eq.other.x = extractvalue %ori.Point %1, 0
  %eq.x = icmp eq i64 %eq.self.x, %eq.other.x
  br i1 %eq.x, label %eq.field.1, label %eq.false

eq.true:
  ret i1 true

eq.false:
  ret i1 false

eq.field.1:
  %eq.self.y = extractvalue %ori.Point %0, 1
  %eq.other.y = extractvalue %ori.Point %1, 1
  %eq.y = icmp eq i64 %eq.self.y, %eq.other.y
  br i1 %eq.y, label %eq.true, label %eq.false
}

; Function Attrs: nounwind uwtable
; --- Color.@eq ---
define fastcc noundef i1 @"_ori_Color$eq"(%ori.Color noundef %0, %ori.Color noundef %1) #1 {
entry:
  %eq.tag.self = extractvalue %ori.Color %0, 0
  %eq.tag.other = extractvalue %ori.Color %1, 0
  %eq.tags = icmp eq i64 %eq.tag.self, %eq.tag.other
  br i1 %eq.tags, label %eq.true, label %eq.false

eq.true:
  ret i1 true

eq.false:
  ret i1 false
}

; Function Attrs: nounwind uwtable
; --- Shape.@eq ---
define fastcc noundef i1 @"_ori_Shape$eq"(ptr noundef nonnull dereferenceable(24) %0, ptr noundef nonnull dereferenceable(24) %1) #1 {
entry:
  %param.0 = load %ori.Shape, ptr %0, align 8
  %param.1 = load %ori.Shape, ptr %1, align 8
  %eq.tag.self = extractvalue %ori.Shape %param.0, 0
  %eq.tag.other = extractvalue %ori.Shape %param.1, 0
  %eq.tags = icmp eq i64 %eq.tag.self, %eq.tag.other
  br i1 %eq.tags, label %eq.tags.match, label %eq.false

eq.true:
  ret i1 true

eq.false:
  ret i1 false

eq.tags.match:
  %eq.self.payload = extractvalue %ori.Shape %param.0, 1
  %eq.other.payload = extractvalue %ori.Shape %param.1, 1
  switch i64 %eq.tag.self, label %eq.false [
    i64 0, label %eq.v.Circle
    i64 1, label %eq.v.Rect
  ]

eq.v.Circle:
  %eq.v0.self.f0 = extractvalue [2 x i64] %eq.self.payload, 0
  %eq.v0.other.f0 = extractvalue [2 x i64] %eq.other.payload, 0
  %eq.v0.f0 = icmp eq i64 %eq.v0.self.f0, %eq.v0.other.f0
  br i1 %eq.v0.f0, label %eq.true, label %eq.false

eq.v.Rect:
  %eq.v1.self.f0 = extractvalue [2 x i64] %eq.self.payload, 0
  %eq.v1.other.f0 = extractvalue [2 x i64] %eq.other.payload, 0
  %eq.v1.f0 = icmp eq i64 %eq.v1.self.f0, %eq.v1.other.f0
  br i1 %eq.v1.f0, label %eq.v1.f1, label %eq.false

eq.v1.f1:
  %eq.v1.self.f1 = extractvalue [2 x i64] %eq.self.payload, 1
  %eq.v1.other.f1 = extractvalue [2 x i64] %eq.other.payload, 1
  %eq.v1.f11 = icmp eq i64 %eq.v1.self.f1, %eq.v1.other.f1
  br i1 %eq.v1.f11, label %eq.true, label %eq.false
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #2

; Function Attrs: cold noreturn
declare void @ori_panic_cstr(ptr) #3

; Function Attrs: nounwind uwtable
define noundef i32 @main() #1 {
entry:
  %ori_main_result = call i64 @_ori_main()
  %exit_code = trunc i64 %ori_main_result to i32
  %leak_check = call i32 @ori_check_leaks()
  %has_leak = icmp ne i32 %leak_check, 0
  %final_exit = select i1 %has_leak, i32 %leak_check, i32 %exit_code
  ret i32 %final_exit
}

; Function Attrs: nounwind
declare i32 @ori_check_leaks() #4

attributes #0 = { nounwind memory(none) uwtable }
attributes #1 = { nounwind uwtable }
attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #3 = { cold noreturn }
attributes #4 = { nounwind }

Disassembly

_ori_check_struct_eq:
   sub    $0x18,%rsp
   mov    $0xa,%edx
   mov    $0x14,%ecx
   mov    %rdx,%rdi
   mov    %rcx,%rsi
   call   <_ori_Point$eq>
   mov    %al,%dl
   xor    %eax,%eax
   mov    $0x3,%ecx
   test   $0x1,%dl
   cmovne %rcx,%rax
   mov    %rax,0x8(%rsp)
   mov    $0x14,%esi
   mov    $0xa,%edx
   mov    $0x1e,%ecx
   mov    %rdx,%rdi
   call   <_ori_Point$eq>
   mov    %al,%sil
   mov    0x8(%rsp),%rax
   xor    $0xff,%sil
   xor    %ecx,%ecx
   mov    $0x4,%edx
   test   $0x1,%sil
   cmovne %rdx,%rcx
   add    %rcx,%rax
   mov    %rax,0x10(%rsp)
   seto   %al
   jo     .overflow
   mov    0x10(%rsp),%rax
   add    $0x18,%rsp
   ret
.overflow:
   lea    ovf.msg(%rip),%rdi
   call   <ori_panic_cstr>

_ori_check_sum_eq:
   sub    $0x18,%rsp
   xor    %eax,%eax
   mov    %eax,%esi
   mov    %rsi,%rdi
   call   <_ori_Color$eq>
   mov    %al,%dl
   xor    %eax,%eax
   mov    $0x5,%ecx
   test   $0x1,%dl
   cmovne %rcx,%rax
   mov    %rax,0x8(%rsp)
   xor    %eax,%eax
   mov    %eax,%edi
   mov    $0x2,%esi
   call   <_ori_Color$eq>
   mov    %al,%sil
   mov    0x8(%rsp),%rax
   xor    $0xff,%sil
   xor    %ecx,%ecx
   mov    $0x6,%edx
   test   $0x1,%sil
   cmovne %rdx,%rcx
   add    %rcx,%rax
   mov    %rax,0x10(%rsp)
   seto   %al
   jo     .overflow
   mov    0x10(%rsp),%rax
   add    $0x18,%rsp
   ret
.overflow:
   lea    ovf.msg(%rip),%rdi
   call   <ori_panic_cstr>

_ori_check_nested:
   sub    $0x78,%rsp
   movq   $0x0,0x28(%rsp)      ; s1.payload[1] = 0
   movq   $0xa,0x20(%rsp)      ; s1.payload[0] = 10
   movq   $0x0,0x18(%rsp)      ; s1.tag = 0 (Circle)
   movq   $0x0,0x40(%rsp)      ; s2.payload[1] = 0
   movq   $0xa,0x38(%rsp)      ; s2.payload[0] = 10
   movq   $0x0,0x30(%rsp)      ; s2.tag = 0 (Circle)
   lea    0x18(%rsp),%rdi      ; &s1
   lea    0x30(%rsp),%rsi      ; &s2
   call   <_ori_Shape$eq>
   ; ... select, overflow check, return ...

_ori_main:
   sub    $0x28,%rsp
   call   <_ori_check_struct_eq>
   mov    %rax,0x10(%rsp)
   call   <_ori_check_sum_eq>
   mov    %rax,0x8(%rsp)
   call   <_ori_check_nested>
   mov    0x8(%rsp),%rcx
   mov    %rax,%rdx
   mov    0x10(%rsp),%rax
   mov    %rdx,0x18(%rsp)
   add    %rcx,%rax            ; a + b
   mov    %rax,0x20(%rsp)
   seto   %al
   jo     .overflow1
   mov    0x18(%rsp),%rcx
   mov    0x20(%rsp),%rax
   add    %rcx,%rax            ; (a+b) + c
   mov    %rax,(%rsp)
   seto   %al
   jo     .overflow2
   mov    (%rsp),%rax
   add    $0x28,%rsp
   ret

_ori_Point$eq:
   mov    %rcx,-0x10(%rsp)     ; save other.y
   mov    %rsi,-0x8(%rsp)      ; save self.y
   cmp    %rdx,%rdi            ; self.x == other.x?
   je     .field1
   xor    %eax,%eax            ; return false
   ret
.field1:
   mov    -0x8(%rsp),%rax
   mov    -0x10(%rsp),%rcx
   cmp    %rcx,%rax            ; self.y == other.y?
   je     .true
   xor    %eax,%eax            ; return false
   ret
.true:
   mov    $0x1,%al             ; return true
   ret

_ori_Color$eq:
   cmp    %rsi,%rdi            ; tag1 == tag2?
   jne    .false
   mov    $0x1,%al
   ret
.false:
   xor    %eax,%eax
   ret

_ori_Shape$eq:
   mov    0x10(%rdi),%rax      ; load self.payload[1]
   mov    (%rdi),%rax          ; load self.tag
   mov    0x8(%rdi),%rcx       ; load self.payload[0]
   mov    0x10(%rsi),%rcx      ; load other.payload[1]
   mov    (%rsi),%rcx          ; load other.tag
   mov    0x8(%rsi),%rdx       ; load other.payload[0]
   cmp    %rcx,%rax            ; tags equal?
   je     .tags_match
   xor    %eax,%eax            ; return false
   ret
.tags_match:
   test   %rax,%rax            ; tag == 0 (Circle)?
   je     .circle
   sub    $0x1,%rax            ; tag == 1 (Rect)?
   je     .rect
   xor    %eax,%eax            ; default: false
   ret
.circle:
   ; compare payload[0] (radius)
   cmp    %rcx,%rax
   je     .true
   xor    %eax,%eax
   ret
.rect:
   ; compare payload[0] (w)
   cmp    %rcx,%rax
   jne    .false
   ; compare payload[1] (h)
   cmp    %rcx,%rax
   je     .true
   xor    %eax,%eax
   ret
.true:
   mov    $0x1,%al
   ret

Deep Scrutiny

1. Instruction Purity

#FunctionActualIdealRatioVerdict
1@check_struct_eq12121.00xOPTIMAL
2@check_sum_eq12121.00xOPTIMAL
3@check_nested20201.00xOPTIMAL
4@main16161.00xOPTIMAL
5Point$eq10101.00xOPTIMAL
6Color$eq661.00xOPTIMAL
7Shape$eq23231.00xOPTIMAL

All functions score OPTIMAL. The generated derived trait methods are lean:

  • Point$eq: 10 instructions for 2-field struct comparison with short-circuit (extract, compare, branch per field)
  • Color$eq: 6 instructions for unit-variant enum (single tag comparison)
  • Shape$eq: 23 instructions for payload-carrying sum type (2 for direct load %ori.Shape, 4 for tag extraction + comparison, 3 for switch dispatch, 14 for two variant comparison paths with short-circuit)

The caller functions (check_struct_eq, check_sum_eq, check_nested) each follow the same clean pattern: call $eq, select result, add with overflow check.

Improvement since prior run: Shape$eq dropped from 33 to 23 instructions. The previous codegen used per-field GEP+load+insertvalue reconstruction (12 instructions) to build %ori.Shape values from pointers; the current codegen uses direct load %ori.Shape (2 instructions). This 10-instruction reduction is a genuine codegen improvement.

2. ARC Purity

Functionrc_incrc_decBalancedBorrow ElisionMove Semantics
@check_struct_eq00YESN/AN/A
@check_sum_eq00YESN/AN/A
@check_nested00YESN/AN/A
@main00YESN/AN/A
Point$eq00YESN/AN/A
Color$eq00YESN/AN/A
Shape$eq00YESN/AN/A

Verdict: All types contain only int fields — no heap allocations, zero RC operations. OPTIMAL.

3. Attributes & Calling Convention

Functionfastccnounwinduwtablememorynoundef(ret)noundef(params)coldNotes
@check_struct_eqYESYESYESnoneYESN/ANO[NOTE-6] pure function
@check_sum_eqYESYESYESnoneYESN/ANO[NOTE-6] pure function
@check_nestedYESYESYESnoneYESN/ANO[NOTE-6] pure function
@mainC-ccYESYESYESN/ANOEntry point — C calling convention correct
Point$eqYESYESYESYESYES (both)NO
Color$eqYESYESYESYESYES (both)NO
Shape$eqYESYESYESYESYES (both ptr)NO[NOTE-1] nonnull + dereferenceable(24)
@ori_panic_cstrC-ccYESnoreturn + cold correct
@main (wrapper)C-ccYESYESYESN/ANO

Compliance: 32/32 applicable attributes correct (100%).

Key attributes:

  • check_struct_eq, check_sum_eq, check_nested marked memory(none) — correctly identified as pure functions
  • Shape$eq ptr parameters carry noundef nonnull dereferenceable(24) — full pointer safety attributes

4. Control Flow & Block Layout

FunctionBlocksEmpty BlocksRedundant BranchesPhi NodesNotes
@check_struct_eq3000
@check_sum_eq3000
@check_nested3000
@main5000Two overflow check chains
Point$eq4000Short-circuit: x then y
Color$eq3000Single tag compare
Shape$eq7000Tag check -> switch -> per-variant

Verdict: Clean control flow throughout. No empty blocks, no redundant branches, no trivial phi nodes.

Point$eq uses an excellent short-circuit pattern: compare x first, skip y comparison if x differs. The block layout (entry -> eq.field.1 -> eq.true/eq.false) is optimal.

Shape$eq uses a textbook discriminant dispatch pattern: compare tags first, then switch on tag value to per-variant comparison blocks. Each variant block compares fields with short-circuit. The 7-block layout is the minimum needed for this logic.

5. Overflow Checking

Status: PASS

OperationCheckedCorrectNotes
add (check_struct_eq)YESYESllvm.sadd.with.overflow.i64
add (check_sum_eq)YESYESllvm.sadd.with.overflow.i64
add (check_nested)YESYESllvm.sadd.with.overflow.i64
add (main, first)YESYESllvm.sadd.with.overflow.i64
add (main, second)YESYESllvm.sadd.with.overflow.i64

All 5 integer additions use checked overflow with llvm.sadd.with.overflow.i64, branching to ori_panic_cstr on overflow. Correct.

6. Binary Analysis

MetricValue
Binary size6.25 MiB (debug)
.text section870.1 KiB
.rodata section133.5 KiB
User code~750 bytes (7 user functions)
Runtime99.9% of binary

Disassembly: Point$eq (48 bytes)

_ori_Point$eq:
  mov    %rcx,-0x10(%rsp)
  mov    %rsi,-0x8(%rsp)
  cmp    %rdx,%rdi             ; self.x == other.x?
  je     .check_y
  xor    %eax,%eax             ; return false
  ret
.check_y:
  mov    -0x8(%rsp),%rax
  mov    -0x10(%rsp),%rcx
  cmp    %rcx,%rax             ; self.y == other.y?
  je     .true
  xor    %eax,%eax             ; return false
  ret
.true:
  mov    $0x1,%al              ; return true
  ret

Point is passed by value as 4 registers (rdi=x_self, rsi=y_self, rdx=x_other, rcx=y_other via fastcc). The function spills y fields to the red zone and does two sequential comparisons. Clean, branchless fallthrough on mismatch.

Disassembly: Color$eq (11 bytes)

_ori_Color$eq:
  cmp    %rsi,%rdi             ; tag1 == tag2?
  jne    .false
  mov    $0x1,%al              ; return true
  ret
.false:
  xor    %eax,%eax             ; return false
  ret

Minimal — a single cmp+jne for unit-variant enum equality. 11 bytes total. Optimal.

Disassembly: Shape$eq (174 bytes)

Shape is passed by pointer due to its 24-byte size. The native code loads all fields, compares tags, then uses a test+sub chain for variant dispatch (LLVM lowers the switch to sequential comparisons for 2 cases). Each variant path does field-by-field comparison with short-circuit.

7. Optimal IR Comparison

Point$eq: Ideal vs Actual

; IDEAL (10 instructions)
define fastcc noundef i1 @"_ori_Point$eq"(%ori.Point noundef %0, %ori.Point noundef %1) nounwind {
entry:
  %eq.self.x = extractvalue %ori.Point %0, 0
  %eq.other.x = extractvalue %ori.Point %1, 0
  %eq.x = icmp eq i64 %eq.self.x, %eq.other.x
  br i1 %eq.x, label %eq.field.1, label %eq.false
eq.field.1:
  %eq.self.y = extractvalue %ori.Point %0, 1
  %eq.other.y = extractvalue %ori.Point %1, 1
  %eq.y = icmp eq i64 %eq.self.y, %eq.other.y
  br i1 %eq.y, label %eq.true, label %eq.false
eq.true:
  ret i1 true
eq.false:
  ret i1 false
}
; ACTUAL (10 instructions) -- identical structure
define fastcc noundef i1 @"_ori_Point$eq"(%ori.Point noundef %0, %ori.Point noundef %1) #1 {
entry:
  %eq.self.x = extractvalue %ori.Point %0, 0
  %eq.other.x = extractvalue %ori.Point %1, 0
  %eq.x = icmp eq i64 %eq.self.x, %eq.other.x
  br i1 %eq.x, label %eq.field.1, label %eq.false
eq.true:
  ret i1 true
eq.false:
  ret i1 false
eq.field.1:
  %eq.self.y = extractvalue %ori.Point %0, 1
  %eq.other.y = extractvalue %ori.Point %1, 1
  %eq.y = icmp eq i64 %eq.self.y, %eq.other.y
  br i1 %eq.y, label %eq.true, label %eq.false
}

Delta: 0 instructions. Block ordering differs (eq.true/eq.false before eq.field.1 in actual) but this is semantically equivalent and LLVM optimizes layout during code generation.

Color$eq: Ideal vs Actual

; IDEAL (6 instructions)
define fastcc noundef i1 @"_ori_Color$eq"(%ori.Color noundef %0, %ori.Color noundef %1) nounwind {
entry:
  %eq.tag.self = extractvalue %ori.Color %0, 0
  %eq.tag.other = extractvalue %ori.Color %1, 0
  %eq.tags = icmp eq i64 %eq.tag.self, %eq.tag.other
  br i1 %eq.tags, label %eq.true, label %eq.false
eq.true:
  ret i1 true
eq.false:
  ret i1 false
}

Delta: 0 instructions. Actual matches ideal exactly.

Shape$eq: Ideal vs Actual

; IDEAL (23 instructions)
define fastcc noundef i1 @"_ori_Shape$eq"(ptr noundef nonnull dereferenceable(24) %0,
                                          ptr noundef nonnull dereferenceable(24) %1) nounwind {
entry:
  %p0 = load %ori.Shape, ptr %0, align 8
  %p1 = load %ori.Shape, ptr %1, align 8
  %tag0 = extractvalue %ori.Shape %p0, 0
  %tag1 = extractvalue %ori.Shape %p1, 0
  %tags_eq = icmp eq i64 %tag0, %tag1
  br i1 %tags_eq, label %tags_match, label %false
tags_match:
  %pay0 = extractvalue %ori.Shape %p0, 1
  %pay1 = extractvalue %ori.Shape %p1, 1
  switch i64 %tag0, label %false [i64 0, label %circle  i64 1, label %rect]
circle:
  %c0 = extractvalue [2 x i64] %pay0, 0
  %c1 = extractvalue [2 x i64] %pay1, 0
  %ceq = icmp eq i64 %c0, %c1
  br i1 %ceq, label %true, label %false
rect:
  %r0a = extractvalue [2 x i64] %pay0, 0
  %r1a = extractvalue [2 x i64] %pay1, 0
  %req0 = icmp eq i64 %r0a, %r1a
  br i1 %req0, label %rect_f1, label %false
rect_f1:
  %r0b = extractvalue [2 x i64] %pay0, 1
  %r1b = extractvalue [2 x i64] %pay1, 1
  %req1 = icmp eq i64 %r0b, %r1b
  br i1 %req1, label %true, label %false
true:
  ret i1 true
false:
  ret i1 false
}

Delta: 0 instructions. The actual codegen now uses direct load %ori.Shape (2 instructions) instead of the previous per-field GEP+load+insertvalue reconstruction (12 instructions). This is a 10-instruction improvement over the prior run.

check_struct_eq: Ideal vs Actual

; IDEAL (12 instructions)
define fastcc noundef i64 @_ori_check_struct_eq() nounwind memory(none) {
  %derived_eq = call fastcc i1 @"_ori_Point$eq"(...)
  %sel = select i1 %derived_eq, i64 3, i64 0
  %derived_eq1 = call fastcc i1 @"_ori_Point$eq"(...)
  %neq = xor i1 %derived_eq1, true
  %sel2 = select i1 %neq, i64 4, i64 0
  %add = call {i64,i1} @llvm.sadd.with.overflow.i64(i64 %sel, i64 %sel2)
  %val = extractvalue {i64,i1} %add, 0
  %ovf = extractvalue {i64,i1} %add, 1
  br i1 %ovf, label %panic, label %ok
ok:
  ret i64 %val
panic:
  call void @ori_panic_cstr(...)
  unreachable
}

Delta: 0 instructions. check_sum_eq and check_nested follow the same pattern.

Module Summary

FunctionIdealActualDeltaJustifiedVerdict
@check_struct_eq1212+0N/AOPTIMAL
@check_sum_eq1212+0N/AOPTIMAL
@check_nested2020+0N/AOPTIMAL
@main1616+0N/AOPTIMAL
Point$eq1010+0N/AOPTIMAL
Color$eq66+0N/AOPTIMAL
Shape$eq2323+0N/AOPTIMAL

8. Derived Traits: Eq Codegen

Three distinct derived Eq patterns are generated, each optimized for its type structure:

Struct Eq (Point$eq): Field-by-field comparison with short-circuit. Compares x first; if equal, compares y. If any field differs, immediately returns false. This is the textbook pattern — no wasted work on subsequent fields when an early field already proves inequality.

Unit-variant Sum Eq (Color$eq): Pure tag comparison. Since Red, Green, Blue carry no payload, equality is just tag_self == tag_other. This compiles to a single icmp eq i64 — the simplest possible dispatch.

Payload Sum Eq (Shape$eq): Tag-first, then per-variant field comparison via switch. The codegen correctly handles:

  • Different tags -> immediately false (no payload inspection)
  • Same tag -> dispatch to the correct variant’s comparison logic
  • Circle(radius:) -> compare 1 field
  • Rect(w:, h:) -> compare 2 fields with short-circuit

The != operator desugars cleanly to xor i1 %eq_result, true, avoiding a separate $ne method.

Passing convention: Point (16 bytes) and Color (8 bytes) are passed by value. Shape (24 bytes) is passed by pointer with noundef nonnull dereferenceable(24) safety attributes, requiring alloca+store at call sites and load %ori.Shape inside Shape$eq. This is architecturally correct for the 16-byte by-value threshold. The full pointer attributes enable LLVM to assume the pointers are valid and non-null without runtime checks.

Codegen improvement: Shape$eq now uses direct load %ori.Shape (2 instructions) instead of per-field GEP+load+insertvalue reconstruction (12 instructions in the prior run). The total instruction count dropped from 33 to 23. LLVM generates identical native code either way (it optimizes both patterns to sequential register loads), but the cleaner IR is preferable for readability and reduces optimization burden.

9. Sum Types: Discriminant Dispatch

The compiler uses a consistent tagged-union representation:

TypeLLVM TypeTag FieldPayload
Color{ i64 }Field 0 (i64)None
Shape{ i64, [2 x i64] }Field 0 (i64)Field 1 (max payload)

Tag encoding: Sequential from 0. Red=0, Green=1, Blue=2. Circle=0, Rect=1.

Payload sizing: Shape’s [2 x i64] payload is sized to the largest variant (Rect with 2 fields). Circle uses only payload[0] (radius); payload[1] is unused/zero-initialized.

Switch dispatch: In Shape$eq, after tag comparison, the switch i64 %eq.tag.self instruction dispatches to per-variant blocks. The default case falls through to eq.false, which is defensive (handles potential future variants or invalid tags). LLVM lowers this 2-case switch to a sequential test/sub chain in the native code, which is optimal for small case counts.

Construction: Variant construction uses inline aggregate constants with the correct tag and payload layout:

  • Circle(radius: 10) -> %ori.Shape { i64 0, [2 x i64] [i64 10, i64 0] }
  • Rect(w: 5, h: 8) -> %ori.Shape { i64 1, [2 x i64] [i64 5, i64 8] }

Findings

#SeverityCategoryDescriptionStatusFirst Seen
1NOTEAttributesShape$eq ptr params have noundef + nonnull + dereferenceable(24)CONFIRMEDJ11
2NOTEAttributesmain wrapper return has noundefCONFIRMEDJ1
3NOTECodegenPoint$eq short-circuit is textbook optimalCONFIRMEDJ11
4NOTECodegenColor$eq single-icmp for unit-variant enumCONFIRMEDJ11
5NOTECodegenShape$eq discriminant dispatch with per-variant comparisonCONFIRMEDJ11
6NOTEAttributescheck_struct_eq/check_sum_eq/check_nested marked memory(none)CONFIRMEDJ11
7NOTECodegenShape$eq improved from 33 to 23 instructions (direct load vs GEP reconstruction)NEWJ11

NOTE-1: Shape$eq ptr params with full safety attributes

Location: @"_ori_Shape$eq"(ptr noundef nonnull dereferenceable(24) %0, ptr noundef nonnull dereferenceable(24) %1) Impact: Positive — LLVM can assume pointers are defined, non-null, and point to valid 24-byte regions, enabling better alias analysis and optimization Found in: Attributes & Calling Convention (Category 3)

NOTE-2: main wrapper return has noundef

Location: @main() C entry point wrapper, noundef i32 return Impact: Positive — LLVM can assume exit code is defined Found in: Attributes & Calling Convention (Category 3)

NOTE-3: Point$eq short-circuit pattern

Location: @"_ori_Point$eq" — field-by-field comparison Impact: Positive — avoids comparing y when x already differs Found in: Derived Traits: Eq Codegen (Category 8)

NOTE-4: Color$eq minimal unit-variant comparison

Location: @"_ori_Color$eq" — pure tag comparison Impact: Positive — single icmp eq for 3-variant unit enum, no payload overhead Found in: Derived Traits: Eq Codegen (Category 8)

NOTE-5: Shape$eq discriminant dispatch

Location: @"_ori_Shape$eq" — tag check + switch + per-variant comparison Impact: Positive — textbook sum type equality with short-circuit at every level Found in: Sum Types: Discriminant Dispatch (Category 9)

NOTE-6: Pure functions marked memory(none)

Location: @_ori_check_struct_eq, @_ori_check_sum_eq, @_ori_check_nested Impact: Positive — the nounwind + memory analysis pass correctly identifies these functions as having no memory side effects. All three construct values inline (no heap), call pure $eq methods, and do overflow-checked arithmetic. The memory(none) attribute enables LLVM to freely reorder, CSE, or eliminate these calls. Found in: Attributes & Calling Convention (Category 3)

NOTE-7: Shape$eq codegen improvement (direct load)

Location: @"_ori_Shape$eq" entry block Impact: Positive — previous codegen used 12-instruction per-field GEP+load+insertvalue sequence to reconstruct %ori.Shape from pointer parameters; current codegen uses 2-instruction direct load %ori.Shape. Reduces instruction count from 33 to 23 while producing identical native code. Found in: Derived Traits: Eq Codegen (Category 8)

Codegen Quality Score

CategoryWeightScoreNotes
Instruction Efficiency15%10/101.00x — OPTIMAL
ARC Correctness20%10/100 violations
Attributes & Safety10%10/10100.0% compliance
Control Flow10%10/100 defects
IR Quality20%10/100 unjustified instructions
Binary Quality10%10/100 defects
Other Findings15%10/10No uncategorized findings

Overall: 10.0 / 10

Verdict

Journey 11’s derived trait codegen achieves a perfect score. All three Eq patterns — struct field-by-field, unit-variant tag comparison, and payload sum type discriminant dispatch — generate optimal IR with zero unnecessary instructions. The short-circuit evaluation in Point$eq and Shape$eq avoids wasted work, and the != operator desugars cleanly to xor i1 %result, true without a separate method. Attribute compliance is 100%: Shape$eq ptr parameters carry full noundef nonnull dereferenceable(24) safety attributes, and the three check functions are correctly marked memory(none) by the purity analysis pass. ARC is perfectly irrelevant — all types are pure value types with no heap allocations. Since the prior run, Shape$eq improved from 33 to 23 instructions thanks to direct load %ori.Shape replacing per-field GEP reconstruction.

Cross-Journey Observations

FeatureFirst TestedThis JourneyStatus
Overflow checkingJ1J11CONFIRMED
fastcc on internal functionsJ1J11CONFIRMED
nounwind on user functionsJ1J11CONFIRMED
noundef on main wrapperJ1J11CONFIRMED
noundef + nonnull + deref on ptr paramsJ11J11CONFIRMED
memory(none) on pure functionsJ11J11CONFIRMED
Struct field accessJ4J11CONFIRMED (extractvalue pattern)
Sum type tag dispatchJ6J11CONFIRMED (switch on discriminant)

The AIMS purity analysis correctly identifies check_struct_eq, check_sum_eq, and check_nested as having no memory effects, marking them memory(none). The codegen for Shape$eq has improved since the prior run, now using direct aggregate loads instead of per-field GEP reconstruction.