# Pitch: Add bidiClass to Unicode.Scalar.Properties

**URL:** <https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097>\
**Category:** Pitches\
**Created:** [August 20, 2026, 8:38pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097 "2026-08-20T20:38:55Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Michael\_Ilseman](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/michael_ilseman/32/44640_2.png) [@Michael\_Ilseman](https://forums.swift.org/u/Michael_Ilseman)\
**Post date:** [August 20, 2026, 8:38pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/1 "2026-08-20T20:38:56Z")

</div>

Thanks to Chris Chapman (@cjchapman) for driving this!

[gist](https://gist.github.com/milseman/fa48ef38a0f708e16eaf2b5dce476172)

* * *

# Add `bidiClass` to `Unicode.Scalar.Properties`

- Proposal: SE-NNNN
- Authors: [Chris Chapman](https://github.com/cjchapman), [Michael Ilseman](https://github.com/milseman)
- Review Manager: TBD
- Status: **Awaiting review**
- Implementation: [swiftlang/swift#91404](https://github.com/swiftlang/swift/pull/91404)
- Review: ([pitch](https://forums.swift.org/...))

## Introduction

`Unicode.Scalar.Properties` exposes many of the scalar properties defined by the Unicode Standard, but not `Bidi_Class`, the classification that drives the Unicode Bidirectional Algorithm (UAX #9). This proposal adds a `bidiClass` property and a `Unicode.BidiClass` enum for its values.

## Motivation

Code that lays out or transforms bidirectional text needs each scalar's `Bidi_Class`. An example task is deciding whether a piece of text must be wrapped in isolate controls (`U+2068`…`U+2069`) before being embedded in surrounding text of the opposite direction, so that it does not reorder its neighbors. That decision requires knowing whether the text contains strong-directional or numeric scalars, which is exactly what `Bidi_Class` reports. The closely related `generalCategory` is not a substitute: it does not distinguish a strong left-to-right letter from a strong right-to-left one.

Today this value must come from elsewhere, and the most direct source, the system's ICU library, is the same pain point described in SE-0211. Swift's Foundation ran into this when implementing list-item isolate wrapping in `ListFormatStyle`.

## Proposed solution

We add a `bidiClass` computed property to `Unicode.Scalar.Properties`, and a `Unicode.BidiClass` enum, mirroring the existing `generalCategory` property and `Unicode.GeneralCategory` enum:

```swift
let scalar: Unicode.Scalar = "\u{05D0}" // HEBREW LETTER ALEF
scalar.properties.bidiClass // .rightToLeft

```

Every scalar has a defined `Bidi_Class`: scalars not explicitly assigned one, including unassigned code points, take a code-point-based default. So `bidiClass` returns a value for every `Unicode.Scalar`.

## Detailed design

`Unicode.BidiClass` has one case per `Bidi_Class` value, with names derived from the property's long value names, each documented with its standard abbreviation (as `Unicode.GeneralCategory` does with the two-letter codes):

```swift
extension Unicode {

  /// The classification of a scalar used by the Unicode Bidirectional
  /// Algorithm.
  @available(SwiftStdlib 6.5, *)
  public enum BidiClass: Hashable, Sendable {
    case leftToRight // L
    case rightToLeft // R
    case arabicLetter // AL
    case europeanNumber // EN
    case europeanSeparator // ES
    case europeanTerminator // ET
    case arabicNumber // AN
    case commonSeparator // CS
    case nonspacingMark // NSM
    case boundaryNeutral // BN
    case paragraphSeparator // B
    case segmentSeparator // S
    case whitespace // WS
    case otherNeutral // ON
    case leftToRightEmbedding // LRE
    case leftToRightOverride // LRO
    case rightToLeftEmbedding // RLE
    case rightToLeftOverride // RLO
    case popDirectionalFormat // PDF
    case leftToRightIsolate // LRI
    case rightToLeftIsolate // RLI
    case firstStrongIsolate // FSI
    case popDirectionalIsolate // PDI
  }
}

extension Unicode.Scalar.Properties {

  /// The bidirectional class of the scalar.
  ///
  /// This property corresponds to the "Bidi_Class" property in the
  /// [Unicode Standard](http://www.unicode.org/versions/latest/).
  @available(SwiftStdlib 6.5, *)
  public var bidiClass: Unicode.BidiClass { get }
}

```

The values follow the Unicode Character Database's `DerivedBidiClass.txt`, including its `@missing` default rules.

## Source compatibility

This proposal is purely additive and has no source compatibility impact.

## Effect on ABI stability

This proposal is purely an extension of the ABI of the standard library. `Unicode.BidiClass` is a non-frozen enum (like `Unicode.GeneralCategory`), so future values can be added without breaking ABI. No raw representation is exposed, leaving the storage free to change.

---

<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 20, 2026, 9:29pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/2 "2026-08-20T21:29:14Z")

</div>

Would there be any utility in providing also the 'strength' of each of these classes (i.e., strong, weak, neutral, explicit) per UAX#9?

---

<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 20, 2026, 9:59pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/3 "2026-08-20T21:59:08Z")

</div>

Is there a binary size cost to this (on non-Apple platforms), or is this already included in the data the stdlib has, or is it _not_ included but will be dead-strippable?

---

<div class="post-metadata">

**Author:** ![cjchapman](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cjchapman/32/46336_2.png) [@cjchapman](https://forums.swift.org/u/cjchapman)\
**Post date:** [August 21, 2026, 9:27pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/4 "2026-08-21T21:27:42Z")

</div>

Bidi class is the normative Unicode property, and strength is just an informal grouping in the UAX #9 prose that can be derived directly from the class. ICU works this way too: its bidi implementation stores only the class and derives the groupings it needs. Since stdlib API is effectively permanent, we'd rather keep the initial surface minimal and not add categories speculatively. If we find a concrete need later, we can add an API for them without shipping any new data.

---

<div class="post-metadata">

**Author:** ![cjchapman](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cjchapman/32/46336_2.png) [@cjchapman](https://forums.swift.org/u/cjchapman)\
**Post date:** [August 24, 2026, 2:42pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/5 "2026-08-24T14:42:39Z")

</div>

The bidi class is stored as an inversion list that adds about 5k of data to the `Unicode.Scalar.Properties`. I _think_ the bidi class data should be dead-strippable in a static-link scenario. In that respect it will behave the same as other `Unicode.Scalar.Properties` like general category.

---

<div class="post-metadata">

**Author:** ![Alejandro](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/alejandro/32/46197_2.png) [@Alejandro](https://forums.swift.org/u/Alejandro)\
**Post date:** [August 27, 2026, 7:37pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/6 "2026-08-27T19:37:30Z")

</div>

I think we should make this new `BidiClass` type a frozen struct that wraps your choice of `UInt8`/`UInt16`/`UInt32`/`UInt64` (probably `UInt64`) and provides computed static variables of each "enum case". The performance of resilient enums is not ideal and by making a frozen struct of `UInt64` we can shave off a ton of the performance regressions by being resilient. Unfortunately `Unicode.Scalar.Properties` is also resilient so by simply accessing `scalar.properties` you've already hit a performance pitfall, but that doesn't mean we can't make this new type be more performant than its predecessors. Ideally in the future we are able to mark old types as frozen after a particular release. So what I'm imaging is something like the following:

```swift
public struct BidiClass {
  public static var leftToRight: BidiClass {
    get
  }

  ... etc
}

extension BidiClass: Equatable {}

// Can maybe add Hashable? Not sure how useful, but doable at least.

```

Of course leaving a bunch of the performance and ABI attributes out because we don't typically include such things in proposals.

---

<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 27, 2026, 9:01pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/7 "2026-08-27T21:01:32Z")

</div>

> [@Alejandro](#):
>
> The performance of resilient enums is not ideal and by making a frozen struct of `UInt64` we can shave off a ton of the performance regressions by being resilient.

Can we just...fix this instead of baking in workarounds?

---

<div class="post-metadata">

**Author:** ![Alejandro](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/alejandro/32/46197_2.png) [@Alejandro](https://forums.swift.org/u/Alejandro)\
**Post date:** [August 27, 2026, 9:04pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/8 "2026-08-27T21:04:58Z")

</div>

> [@xwu](#):
>
> Can we just...fix this instead of baking in workarounds?

We cannot just fix resilient enums. A client making/getting a value of a resilient type needs to ask the Swift runtime for its metadata (which it potentially has to go allocate itself) to know how large the type is at runtime (because the compiler doesn't know how large the type is at compile time) to make dynamic stack allocations. This is a fundamental property of resilient types and ABI compatibility. With a frozen struct wrapping some `UInt64` clients know exactly how large the type is at compile time and can eliminate asking the Swift runtime anything.

---

<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 27, 2026, 9:18pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/9 "2026-08-27T21:18:26Z")

</div>

> [@Alejandro](#):
>
> We cannot just fix resilient enums.

Clearly. I mean (by adding underscored attributes where needed before necessarily formalizing into a public feature) addressing the underlying problem that we don't have a notion for enums of "frozen in size but not the specific number of cases" and have to essentially open code that with structs. Feels like we're leaving ourselves a known, repeatable performance footgun.

---

<div class="post-metadata">

**Author:** ![Michael\_Ilseman](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/michael_ilseman/32/44640_2.png) [@Michael\_Ilseman](https://forums.swift.org/u/Michael_Ilseman)\
**Post date:** [September 21, 2026, 4:58pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/10 "2026-09-21T16:58:17Z")

</div>

> [@Alejandro](#):
>
> I think we should make this new `BidiClass` type a frozen struct that wraps your choice of `UInt8`/`UInt16`/`UInt32`/`UInt64` (probably `UInt64`)

I'm working with @cjchapman on getting this ready for SE review. Out of curiosity, why `UInt64` as a preference? Despite being someone who has [extensively codified](https://github.com/apple/swift-system/blob/main/Sources/System/Errno.swift) this particular pattern, all my past experience in Swift has had a pre-existing memory layout to be compatible with so I never had to make the choice. Similarly, my past experience doing this in C++ mostly revolved around avoiding bitfield straddling, which I don't think has a direct analogue in Swift today.

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [October 13, 2026, 9:39pm UTC](https://forums.swift.org/t/pitch-add-bidiclass-to-unicode-scalar-properties/89097/11 "2026-10-13T21:39:58Z")

</div>

Can we use an `@c` enum to get a compact layout that's still `@nonexhaustive`?

Doug
