# \`ExpressibleByIntegerLiteral\` and type inference (and macro)

**URL:** <https://forums.swift.org/t/expressiblebyintegerliteral-and-type-inference-and-macro/73698>\
**Category:** Using Swift\
**Tags:** compiler, type-inference\
**Created:** [August 5, 2024, 9:26am UTC](https://forums.swift.org/t/expressiblebyintegerliteral-and-type-inference-and-macro/73698 "2024-08-05T09:26:22Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![YOCKOW](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/yockow/32/20636_2.png) [@YOCKOW](https://forums.swift.org/u/YOCKOW)\
**Post date:** [August 5, 2024, 9:26am UTC](https://forums.swift.org/t/expressiblebyintegerliteral-and-type-inference-and-macro/73698/1 "2024-08-05T09:26:22Z")

</div>

Out of curiosity, I conducted some experiments about `ExpressibleByIntegerLiteral` and type inference.  
Given the following code:

```swift
struct MyUInt: ExpressibleByIntegerLiteral {
  // Not conforms to `AdditiveArithmetic`
  typealias IntegerLiteralType = UInt
  init(integerLiteral: IntegerLiteralType) {}
}

struct MyPlusInt {}
extension MyUInt {
  static prefix func +(_: MyUInt) -> MyPlusInt { .init() }
}

struct MyMinusInt {}
extension MyUInt {
  static prefix func -(_: MyUInt) -> MyMinusInt { .init() }
}

func f(_: MyUInt) { print("Unsigned") }
func f(_: MyPlusInt) { print("+") }
func f(_: MyMinusInt) { print("-") }

@freestanding(expression) macro m(_: MyUInt) -> UInt = #externalMacro(module: "MyMacros", type: "MyWonderfulMacro")
@freestanding(expression) macro m(_: MyPlusInt) -> UInt = #externalMacro(module: "MyMacros", type: "MyWonderfulMacro")
@freestanding(expression) macro m(_: MyMinusInt) -> Int = #externalMacro(module: "MyMacros", type: "MyWonderfulMacro")

/* (0) */ let _: MyUInt = -1 // ❌ error: negative integer '-1' overflows when stored into unsigned type 'MyUInt'
/* (1) */ let _: MyUInt = 1 // ✅ OK
/* (2) */ let _: MyPlusInt = +1 // ✅ OK
/* (3) */ let _: MyMinusInt = -1 // ❌ error: cannot convert value of type 'Int' to specified type 'MyMinusInt'
/* (4) */ let _: MyMinusInt = -(1) // ✅ OK

/* (i) */ f(1) // ✅ Prints "Unsigned"
/* (ii) */ f(+1) // ✅ Prints "+"
/* (iii) */ f(-1) // ❌ error: negative integer '-1' overflows when stored into unsigned type 'MyUInt'
/* (iv) */ f(-(1)) // ✅ Prints "-"

/* (I) */ let _ = #m(1) // ✅ `m(_: MyUInt) -> UInt` is chosen.
/* (II) */ let _ = #m(+1) // ✅ `m(_: MyPlusInt) -> UInt` is chosen.
/* (III) */ let _ = #m(-1) // 🤯 `m(_: MyUInt) -> UInt` is chosen. 🐛❓
/* (IV) */ let _ = #m(-(1)) // ✅ `m(_: MyMinusInt) -> Int` is chosen.

```

### Results and thoughts.

Results are described as the comments in the code.  
My consideration is below:

- `(0)`: It is an error as I expected.
- `(3)`: As left side is explicit about the type, I want the compiler to infer the type like `(4)`.
- `(iii)`: Same as above.
- `(III)`: The weirdest thing happened! While `MyUInt` must not be negative as the compiler said in `(0)`, the compiler chose `m(_: MyUInt) -> UInt` macro that takes an argument with type `MyUInt`. I'm not sure why.

Do you expect these results?

---

<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 5, 2024, 12:47pm UTC](https://forums.swift.org/t/expressiblebyintegerliteral-and-type-inference-and-macro/73698/2 "2024-08-05T12:47:10Z")

</div>

> [@YOCKOW](#):
>
> Do you expect these results?

From my [(slightly old) notes](https://numerics.diploid.ca/integers-part-1.html)—

> Negative values can be represented by prepending the hyphen-minus character (`-`). This is considered to be part of the integer literal. In other words, the expression `-42` is lexed as a single value, not as a call to the prefix operator `-` with `42` as its operand.
> 
> By contrast, the expression `-(42)` is lexed as a call to the prefix operator `-`. [...]
> 
> Note that the Swift standard library defines the prefix operator `+` for symmetry but does _not_ consider a prepended `+` to be part of an integer literal. Therefore, `+42` _is_ lexed as a call to the prefix operator `+` with `42` as its operand.

---

<div class="post-metadata">

**Author:** ![benrimmington](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/benrimmington/32/695_2.png) [@benrimmington](https://forums.swift.org/u/benrimmington)\
**Post date:** [August 5, 2024, 3:28pm UTC](https://forums.swift.org/t/expressiblebyintegerliteral-and-type-inference-and-macro/73698/3 "2024-08-05T15:28:15Z")

</div>

Your `prefix +` will also break type inference, as previously [discussed](https://forums.swift.org/t/accepted-amendment-se-0368-staticbigint/63246).

```swift
let b = +1
// expected `b: Int`
// actually `b: MyPlusInt`

```

(Although this doesn't occur in the Swift 6.0 REPL, for whatever reason.)

---

<div class="post-metadata">

**Author:** ![YOCKOW](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/yockow/32/20636_2.png) [@YOCKOW](https://forums.swift.org/u/YOCKOW)\
**Post date:** [August 6, 2024, 1:37am UTC](https://forums.swift.org/t/expressiblebyintegerliteral-and-type-inference-and-macro/73698/4 "2024-08-06T01:37:16Z")

</div>

@xwu Thank you for sharing your interesting article.  
What confused me was that AST generated by `swift-syntax` is `PrefixOperatorExpr '-'` followed by `IntegerLiteralExpr '1'`.  
Also I thought that the parser could theoretically re-parse it and the type-checker could re-consider it as generally expected even if the lexer recognize it as an integer literal.  
Now I understand [negative numeric literals have special treatment](https://forums.swift.org/t/amendment-se-0368-staticbigint/62992/12) (which I doubt is good idea).  
I completely forgot that I had already read [the related thread](https://forums.swift.org/t/pitch-amend-se-0368-to-remove-prefix-operator/62173)...😅 (thanks @benrimmington )

* * *

Nevertheless macro overload solution seems to have potentially some bugs.

---

<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 6, 2024, 1:14pm UTC](https://forums.swift.org/t/expressiblebyintegerliteral-and-type-inference-and-macro/73698/5 "2024-08-06T13:14:29Z")

</div>

> [@YOCKOW](#):
>
> What confused me was that AST generated by `swift-syntax` is `PrefixOperatorExpr '-'` followed by `IntegerLiteralExpr '1'`.

That seems worthy of filing a bug, maybe?

---

<div class="post-metadata">

**Author:** ![YOCKOW](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/yockow/32/20636_2.png) [@YOCKOW](https://forums.swift.org/u/YOCKOW)\
**Post date:** [August 8, 2024, 1:33am UTC](https://forums.swift.org/t/expressiblebyintegerliteral-and-type-inference-and-macro/73698/6 "2024-08-08T01:33:23Z")

</div>

Just in case, I filed a bug: [swift-syntax doesn't give negative integer literals special treatment while the Swift compiler does. · Issue #2787 · swiftlang/swift-syntax · GitHub](https://github.com/swiftlang/swift-syntax/issues/2787)

* * *

**Follow-up** : @Alex_Hoppen1 [confirmed](https://github.com/swiftlang/swift-syntax/issues/2787#issuecomment-2276618590) that this swift-syntax behavior is intentional.
