# UNDF: UNDF-2026-000000371
# crystal-0003: compute_non_nilable_outside_single — O(N) includes? in O(A) ancestor loop

## Severity: MEDIUM

## Location
- `src/compiler/crystal/semantic/type_declaration_processor.cr:602`
- Called from: `compute_non_nilable_outside` at line 590-594, inside ancestor loop

## Description
`compute_non_nilable_outside_single` builds a deduplicated list of non-nilable instance
variable names using `Array#includes?` — O(N) — for each variable from each ancestor type.
This makes `compute_non_nilable_outside` O(V × A) where V = instance var count, A = ancestor
chain depth. Called once per type during instance variable type declaration processing.

## Root Cause
```crystal
# type_declaration_processor.cr:598-606
private def compute_non_nilable_outside_single(owner, non_nilable_outside)
  if vars = @instance_vars_outside[owner]?
    non_nilable_outside ||= [] of String
    vars.each do |name|
      non_nilable_outside << name unless non_nilable_outside.includes?(name)  # O(N)
    end
  end
  non_nilable_outside
end
```

## Fix
Return a `Set(String)` instead of `Array(String)` (or track seen in a Set alongside):

```crystal
private def compute_non_nilable_outside_single(owner, non_nilable_outside)
  if vars = @instance_vars_outside[owner]?
    non_nilable_outside ||= Set(String).new
    vars.each do |name|
      non_nilable_outside.add(name)   # O(1) set add, handles dedup
    end
  end
  non_nilable_outside
end
```

## Impact
MEDIUM — bounded by instance var count per class. Noticeable in deep class hierarchies
with many instance variables declared outside initializers.
