# \[Pitch\] Dependent Integer Expressions in Generic Arguments

**URL:** <https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170>\
**Category:** Pitches\
**Tags:** generics, integer-generics\
**Created:** [August 25, 2026, 5:32am UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170 "2026-08-25T05:32:57Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Brennanium](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brennanium/32/46377_2.png) [@Brennanium](https://forums.swift.org/u/Brennanium)\
**Post date:** [August 25, 2026, 5:32am UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/1 "2026-08-25T05:32:57Z")

</div>

Hey all,

I've been working on several small prototypes of this ever since [SE-0452](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0452-integer-generic-parameters.md) originally introduced integer generic parameters and mentioned arithmetic generics in the "Future directions" section. Since [SE-0531](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0531-literal-expressions.md) (literal expressions in integer generic argument positions) got accepted recently too, I decided to make another prototype and got far enough this time that I wanted to pitch it for real.

Basically, Swift still cannot use arithmetic with integer generic parameters in generic argument positions to do things like this:

```swift
struct Vector<let count: Int, Element> {}

func concatenated<let lhsCount: Int, let rhsCount: Int, Element>(
    _ lhs: Vector<lhsCount, Element>,
    _ rhs: Vector<rhsCount, Element>
) -> Vector<(lhsCount + rhsCount), Element>

```

The pitch keeps SE-0531's required parentheses, but extends the expressions inside them to include integer generic parameters and a bounded set of `Int` operations. In my prototype, the signature above is accepted, allowing the result type to retain the relationship between its input sizes.

Literal subexpressions continue to use SE-0531's existing folding rules. For example, `Vector<(count + (2 + 3)), Element>` still becomes `Vector<(count + 5), Element>`. The dependent generic part of an expression retains its ordered tree though: `Vector<(n + m), Element>` and `Vector<(m + n), Element>` are distinct. I initially tried to add a more general algebraic-equivalence system, but obviously the complexity of that really ballooned, even when my prototype was just working with `+` and `*` operators. I think keeping the whole expression tree structure ended up making the most sense for many reasons.

I've currently got support for all of the `Int` arithmetic, bitwise, shift, and unary operators supported by the literal expressions from SE-0531. For now, division and remainder require a divisor that SE-0531 can fold to a nonzero integer. Supporting a dependent divisor would require a way to state a `divisor != 0` constraint in a generic signature, which this pitch deliberately leaves out of scope.

This is my first pitch (and first post!) even though I've followed the Swift Evolution forums for several years now. I'm really looking forward to hearing your feedback and thoughts!

The current proposal draft is here:

[https://github.com/Brennanium/swift-evolution/blob/dependent-integer-generic-expressions/proposals/NNNN-dependent-integer-expressions.md](https://github.com/Brennanium/swift-evolution/blob/dependent-integer-generic-expressions/proposals/NNNN-dependent-integer-expressions.md)

The compiler prototype and tests are here:

[https://github.com/Brennanium/swift/tree/dependent-integer-generic-expressions](https://github.com/Brennanium/swift/tree/dependent-integer-generic-expressions)

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [August 26, 2026, 2:29am UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/2 "2026-08-26T02:29:34Z")

</div>

I'm mostly going to stay out of this, but please make sure the compiler doesn't assume `Foo<(n + m)>` and `Foo<(m + n)>` (and `Foo<n>` and `Foo<7>` and `Foo<(a + b + c)>`) are _not_ the same type. That should be fine since `Foo<T>` and `Foo<U>` are already a well-tested version of this, but it'd be really bad to get it wrong. 😅

---

<div class="post-metadata">

**Author:** ![Brennanium](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brennanium/32/46377_2.png) [@Brennanium](https://forums.swift.org/u/Brennanium)\
**Post date:** [August 26, 2026, 5:25am UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/3 "2026-08-26T05:25:39Z")

</div>

Yeah for sure! The compiler does not assume they are unequal once the generic arguments become concrete. `Foo<(n + m)>`and `Foo<(m + n)>` are distinct symbolic forms in a generic context, just like `Foo<n>` and `Foo<7>`. Once the generic arguments are concrete, the expressions evaluate normally, so both `Foo<(3 + 4)>` and `Foo<(4 + 3)>` are `Foo<7>`.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [August 26, 2026, 6:23am UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/4 "2026-08-26T06:23:47Z")

</div>

This looks good. Just want to drop in with two edge-case nits:

> [@](#):
>
> Only an expression whose resolved operator is the corresponding standard library operation on `Int` is accepted. If ordinary lookup selects a user-defined overload, the expression is rejected; the compiler does not perform a second, special lookup for a standard library overload.

It is also currently possible (probably shouldn't be, but alas) for end users to alter the precedence group (and hence associativity and precedence) of stdlib operators locally within their own code. It would probably be right to (and also sufficient to) reject attempts to use stdlib operators in generic contexts when their precedence group has been altered, so as to avoid `2 * 2 + 3` evaluating to different results inside versus outside angle brackets.

> [@](#):
>
> | `Vector<(count << -1), Element>` | Evaluates as `count >> 1`. |
> | --- | --- |

It's sort of implied by your text, but the implementation must be sure that whether this is accepted and evaluates to `count >> 1` depends on whether `<<` resolves to the expected stdlib function, not `>>`.

---

<div class="post-metadata">

**Author:** ![Brennanium](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brennanium/32/46377_2.png) [@Brennanium](https://forums.swift.org/u/Brennanium)\
**Post date:** [August 26, 2026, 4:07pm UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/5 "2026-08-26T16:07:06Z")

</div>

Holy good catch! Allowing local precedence override like that is crazy tbh 😅 I just checked and it appears to not be properly handled by both this prototype and the already accepted SE-0531 literal expressions as well actually. I've got a quick fix for both now.

Also adding a small wording change to not imply that `<<` gets resolved as `>>` for real, just that the result is the same based on normal `<<` smart shift behavior.

---

<div class="post-metadata">

**Author:** ![Brennanium](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brennanium/32/46377_2.png) [@Brennanium](https://forums.swift.org/u/Brennanium)\
**Post date:** [September 1, 2026, 5:40pm UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/6 "2026-09-01T17:40:32Z")

</div>

I've fixed several small bugs and all the tests are passing, also rebased with @ArtemC's recent literal expression fixes in main. Anyone gotten a chance to try out my branch yet?

---

<div class="post-metadata">

**Author:** ![ArtemC](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/artemc/32/27369_2.png) [@ArtemC](https://forums.swift.org/u/ArtemC)\
**Post date:** [September 2, 2026, 2:02pm UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/7 "2026-09-02T14:02:13Z")

</div>

I think the use-cases here are compelling, it would be nice to be able to write:

```swift
extension InlineArray {
  func appended<let k: Int>(_ o: InlineArray<k, Element>)
    -> InlineArray<(count + k), Element>
}

```

A few notes in no particular order:

* * *

The various examples in the pitch put the dependent type in the `return` position, e.g.

```swift
func concatenated<let lhsCount: Int, let rhsCount: Int, Element>(
  _ lhs: Vector<lhsCount, Element>,
  _ rhs: Vector<rhsCount, Element>
) -> Vector<(lhsCount + rhsCount), Element>

```

Can you expand on what happens when putting a dependent generic value expression in parameter position? e.g.

```swift
let v5 = [5 of Int](repeating: 1)
func f<let n: Int>(_ :[(n + 1) of Int]) {}
f(v5) // error: cannot convert value of type '[5 of Int]' to expected argument type '[n + 1 of Int]'
      // error: generic parameter 'n' could not be inferred

```

* * *

> A dependent divisor would require a value constraint such as `divisor != 0` to make that property part of a generic signature; this proposal intentionally does not add value constraints.

> For example, evaluating `Vector<(count + 1), Element>` with `count == Int.max` traps.

Why does division by zero get a different treatment from overflow? It seems that the proposal would be simplified a bit if division by zero got treated the same way as overflow, which gets treated the same way as any other runtime arithmetic fault.

* * *

> An API using the same structural expression can consume the result directly; the proposal does not infer an equivalent differently spelled expression.

Structural identity makes these distinct types:

```swift
func makeSum<let n: Int, let m: Int, Element>(...) -> Vector<(n + m), Element>
func takeSum<let n: Int, let m: Int, Element>(_ v: Vector<(m + n), Element>)

takeSum(makeSum(a, b)) // ?

```

If I understand this correctly, then according to this proposal there isn't a way to do this, which means that two libraries that pick different spellings for the same relationship then can't interoperate.

* * *

In the `| Expression | Behavior |` table:

```swift
| `Vector<(count << -1), Element>` | Has the same result as `count >> 1` (smart shift). |

```

I found this confusing because it conflates identity and evaluation, since this is `true` for the latter and `false` for the former.

---

<div class="post-metadata">

**Author:** ![bbrk24](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bbrk24/32/27370_2.png) [@bbrk24](https://forums.swift.org/u/bbrk24)\
**Post date:** [September 2, 2026, 2:45pm UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/8 "2026-09-02T14:45:10Z")

</div>

> [@ArtemC](#):
>
> Structural identity makes these distinct types:
> 
> ```swift
> func makeSum<let n: Int, let m: Int, Element>(...) -> Vector<(n + m), Element>
> func takeSum<let n: Int, let m: Int, Element>(_ v: Vector<(m + n), Element>)
> 
> takeSum(makeSum(a, b)) // ?
> 
> ```

The second `n` would evaluate to the first `m` and vice versa, right? The issue would only arise if this were in some context where they must be the same thing, e.g:

```swift
struct Foo<let n: Int, let m: Int, Element> {
  func makeSum(...) -> Vector<(n + m), Element> { ... }
  func takeSum(_ v: Vector<(m + n), Element>) { ... }

  func frobnicate() {
    self.takeSum(self.makeSum(...))
  }
}

```

---

<div class="post-metadata">

**Author:** ![Brennanium](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brennanium/32/46377_2.png) [@Brennanium](https://forums.swift.org/u/Brennanium)\
**Post date:** [September 2, 2026, 3:15pm UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/9 "2026-09-02T15:15:14Z")

</div>

@bbrk24 is right in this case; @ArtemC's specific example of structural identity mismatch actually compiles just fine since the generic parameters of both functions are independent from each other:

```swift
func makeSum<let n: Int, let m: Int, Element>() -> Vector<(n + m), Element>
func takeSum<let x: Int, let y: Int, Element>(_ v: Vector<(x + y), Element>)

takeSum(makeSum(a, b)) // compiles just fine n->x and m->y

```

When the generic parameters are already bound like in @bbrk24's struct example, that's when it does pose a problem where `(n + m)` is structurally not equivalent to `(m + n)`

---

<div class="post-metadata">

**Author:** ![Brennanium](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brennanium/32/46377_2.png) [@Brennanium](https://forums.swift.org/u/Brennanium)\
**Post date:** [September 2, 2026, 7:08pm UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/10 "2026-09-02T19:08:34Z")

</div>

Thanks @ArtemC these are all great points! I'll go through and try to address these one by one.

> [@ArtemC](#):
>
> Can you expand on what happens when putting a dependent generic value expression in parameter position? e.g.
> 
> ```swift
> let v5 = [5 of Int](repeating: 1)
> func f<let n: Int>(_ :[(n + 1) of Int]) {}
> f(v5) // error: cannot convert value of type '[5 of Int]' to expected argument type '[n + 1 of Int]'
> // error: generic parameter 'n' could not be inferred
> 
> ```

Including more examples in parameter positions is definitely something I need to expand on in the pitch document and tests. This particular function has no source to infer `n` from since there's no way (yet) for the compiler to solve the equation `n + 1 == 5` to infer `n == 4`. In other examples where we expect to get a `generic parameter X could not be inferred` error:

```swift
func consume<T>(_ value: [T]) {}
consume([]) // error: generic parameter 'T' could not be inferred

```

We can easily fix this by just specifying the type another way like:

```swift
func consume<T>(_ value: [T]) {}
consume([] as [Int]) // no error

```

The problem with your `func f<let n: Int>(_ : [(n + 1) of Int]) {}` example is that in a concrete call with only `v5`, there is no way for the compiler to ever infer `n` without solving `n + 1 == 5`; there's no equivalent of the `as [Int]` example since it's always dependent, and there are no other clues in the function signature to disambiguate. I did initially think that your function signature was actually impossible to resolve concretely, but with some experimenting I did find a situation that does actually compile just fine:

```swift
func f<let n: Int>(_ value: [(n + 1) of Int]) {}

func g<let n: Int>(
  _ first: [n of Int],
  _ second: [(n + 1) of Int]
) {
  f(second)
}

g([4 of Int](repeating: 0), [5 of Int](repeating: 0)) // compiles

```

I didn't expect the compiler to be able to figure out `f.n` like that honestly! The compiler is not solving `f.n + 1 == 5` backward here of course, but it does seem to be able to match `f`’s `(n + 1)` parameter against `g`’s already-known `(n + 1)` parameter and instantiate `f`’s independent `n` with `g`’s symbolic `n` somehow.  
While it does seem a bit strange to have a function signature like `f` be allowed when its use is limited to a very specific context like this, I don't really see a way to cleanly diagnose this as an issue either. Documenting this behavior is important though and I'll definitely be adding some cases to the pitch document to clarify this.

> [@ArtemC](#):
>
> Why does division by zero get a different treatment from overflow? It seems that the proposal would be simplified a bit if division by zero got treated the same way as overflow, which gets treated the same way as any other runtime arithmetic fault.

I think you're right here actually. I'm not sure why the trapping for overflow seemed more natural than trapping for divide by 0, but fundamentally there isn't really a reason why divide by 0 is any more scary than an overflow or anything 😅 looking back it was pretty arbitrary on my part. I already have a branch that allows the full division / remainder functionality; I'll try and get that pulled into the main prototype branch and I'll update the pitch to reflect that too.

> [@ArtemC](#):
>
> Structural identity makes these distinct types:
> 
> ```swift
> func makeSum<let n: Int, let m: Int, Element>(...) -> Vector<(n + m), Element>
> func takeSum<let n: Int, let m: Int, Element>(_ v: Vector<(m + n), Element>)
> 
> takeSum(makeSum(a, b)) // ?
> 
> ```
> 
> If I understand this correctly, then according to this proposal there isn't a way to do this, which means that two libraries that pick different spellings for the same relationship then can't interoperate.

This was partially answered above by @bbrk24's example. In your specific small example, the two free functions can compose since their generic parameters are inferred independently. The limitation only arises when the parameters are already bound in a shared generic context:

```swift
struct Foo<let n: Int, let m: Int, Element> {
  func makeSum(...) -> Vector<(n + m), Element> { ... }
  func takeSum(_ value: Vector<(m + n), Element>) { ... }

  func frobnicate() {
    takeSum(makeSum(...)) // rejected
  }
}

```

There is no current way to prove `n + m` and `m + n` are equivalent here. It was initially _very_ tempting to try to implement some basic algebraic equivalence checking by like canonicalizing the order in subexpressions with commutative operators or grouping constants, but the complexity really exploded and I ran into some serious ABI stability issues. If we start adding some basic algebraic equivalence checking, any tiny change to that algebraic equivalence model later on could retroactively change generic type identity and potentially break ABI.

> [@ArtemC](#):
>
> In the `| Expression | Behavior |` table:
> 
> ```swift
> | `Vector<(count << -1), Element>` | Has the same result as `count >> 1` (smart shift). |
> 
> ```
> 
> I found this confusing because it conflates identity and evaluation, since this is `true` for the latter and `false` for the former.

Yeah... @xwu had similar issues with my explanation before and my attempt to clarify things is still quite confusing I guess. The expression is accepted and retains the structural identity `count << -1`; when evaluated, Swift's smart shift semantics produce the same value as `count >> 1` though.

---

<div class="post-metadata">

**Author:** ![Brennanium](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brennanium/32/46377_2.png) [@Brennanium](https://forums.swift.org/u/Brennanium)\
**Post date:** [September 2, 2026, 10:07pm UTC](https://forums.swift.org/t/pitch-dependent-integer-expressions-in-generic-arguments/89170/11 "2026-09-02T22:07:41Z")

</div>

Updated [code](https://github.com/Brennanium/swift/tree/dependent-integer-generic-expressions) and [pitch document](https://github.com/Brennanium/swift-evolution/tree/dependent-integer-generic-expressions) based on @ArtemC and @bbrk24's comments available now
