Skip to content

Type Casting ​

Implementation status: is type checks / as? failure-to-nil / as! failure-throws / numeric literal inference / Optional wrapping Implemented; the full Swift-style cast set (generic erasure / protocol existential) Partially implemented

Type casting means "treating a value as another type at runtime / compile time". SYX offers three ways: implicit promotion, explicit as / as? / as!, and the is type check.

Numeric Promotion (Implicit) ​

For mixed numeric operations the compiler promotes according to semantic rules (widen_for_binary):

swift
let x: Int = 1
let y = x + 1.0                          // Double (Int → Double)
let z: Int8 = 1
let w = z + 100i32                        // Int32 (Int8 → Int32)
RuleDescription
Integer literalsDefault Int; in operations with Double, Int → Double. Under an explicit Float / Double annotation, an unsuffixed integer is interpreted as that float type (let x: Double = 1)
Signed + unsignedTriggers S014 (avoiding a silent sign-bit wrap)
Integer + floatThe integer is promoted to float
Float + floatSame width returns Double; Float + Double = Double
Int8 + Int8The result is still Int8 (overflow wraps two's complement)
Int8 + IntPromoted to platform Int
Int8 + Int32Promoted to Int32

Same-sign integer narrowing (Int → Int8): allowed in assignment / initialization, via NumericCast; an out-of-range value → runtime R003 (≠ silent truncation). An out-of-range literal is still compile-time S011; a literal with the wrong suffix (8i16 → Int8) is still S004.

Float → integer (Double → Int8): assignment is still not implicit (S004); explicit as / as? truncates toward zero (out-of-range → R003 / nil). S008 is dedicated to cross-actor fields, not to narrowing.

Diagnostics: S014 (mixed signed/unsigned), S004 (incompatible types), R003 (narrowing out of range)

is Type Checks ​

A runtime type check producing a Bool:

swift
let a = [1, 2, 3]
print(a is Array)                         // true
print(a is Array<Int>)                    // true (matched at runtime by base type; element types are not checked through erasure)

class Dog { init() {} }
let d = Dog()
print(d is Dog)                           // true

Numeric targets forbidden: 1 is Int ❌ (compile error S046); is targets classes / protocols / collection base types.

Supported Target Types ​

TargetMatching values
Array / Array<T>Array literals / Value::Array
SetSets
DictionaryDictionaries
String / Bool / Int* / Float* / DoubleThe corresponding scalars
User class / data classInstances carrying __type / ObjectRef
protocol (existential)Values implementing that protocol
swift
let v: any CustomStringConvertible = "hello"
print(v is String)                        // true

Explicit Conversion ​

as? — Failure Becomes nil ​

swift
class Animal { init() {} }
class Dog: Animal {
    override init() { super.init() }
    func bark() { print("woof") }
}

let a: Animal = Dog()
if let d = a as? Dog {
    d.bark()                              // "woof"
}
Scenarioas? behavior
SuccessReturns the target type (Dog)
FailureReturns nil
Same-sign integers (Int as? Int8)Converts when in range; out of range → nil (≠ R003)

as! — Failure Throws ​

swift
class Animal { init() {} }
class Dog: Animal {
    override init() { super.init() }
}

let a: Animal = Animal()
let d = a as! Dog                         // Failure → runtime R003
Scenarioas! behavior
SuccessReturns the target type
FailureRuntime error R003

as — Forced Cast (Known at Compile Time) ​

swift
class Animal { init() {} }
class Dog: Animal {
    override init() { super.init() }
}

let a: Animal = Dog()
let d = a as Dog                          // Known to be Dog; compiles

let n: Int = 8
let n8 = n as Int8                        // Same-sign integer → NumericCast; out of range → R003

let dv: Double = 1.5
let i = dv as Int                          // Truncates toward zero → 1; out of range → R003
let i8 = dv as? Int8                       // Success → 1; out of range → nil

Status: as is used when the conversion is known at compile time; a runtime failure throws. Same-sign integer widening/narrowing and explicit Double/Float → integer go through NumericCast (as out of range → R003; as? out of range → nil). Assignment still does not implicitly convert float→int. The full Swift-style distinction between as / as! / as? is still being refined.

Optional Conversion ​

swift
let x: Int = 42
let y: Int? = x                           // T → T? implicit wrapping
let z: Int = y ?? 0                       // T? → T (nil coalescing)
DirectionFormFailure
T → T?ImplicitNever fails
T? → T?? / ! / if let! failure R007
T → U (related)as? / as!as! → R003

Protocols and Existential Types ​

swift
protocol Describable {
    func describe() -> String
}

class City: Describable {
    let name: String
    init(name: String) { self.name = name }
    func describe() -> String { return name }
}

let c: any Describable = City(name: "Beijing")
if let city = c as? City {
    print(city.name)                      // "Beijing"
}

existential: any Describable is an existential type; dispatch at runtime goes through a witness table. See Chapter 25, Opaque Types.

Numeric Literal Conversion ​

swift
let n: Int = 42
let n8: Int8 = n                          // Same-sign integer narrowing (out of range → R003)
let n8b: Int8 = 42i8                      // The literal suffix selects the type directly
let f: Double = 42.0                      // Explicit float literal
let f2: Double = 3.14                     // Double literal
ConversionForm
Int → Int8Implicit narrowing in assignment/initialization; out of range → R003
Int → Doublewidening: implicit
Double → Intexplicit as / as?: truncates toward zero; out of range as → R003, as? → nil; assignment is still S004
Int8 → Intwidening: implicit
String → Int?Int.fromString("42") → ok(42)
swift
let r = Result.ok(value: "42")
switch (r) {
case .ok(let n) { print("got \(n)") }     // "got 42"
case .err(let e) { print("err: \(e)") }
}

Cross-Class Conversion ​

swift
class Animal { init() {} }
class Dog: Animal { init() { super.init() } }
class Cat: Animal { init() { super.init() } }

let a: Animal = Dog()
if let d = a as? Dog {
    print("it's a dog")
} else if let c = a as? Cat {
    print("it's a cat")
}
  • Subclass → superclass: upcast, always succeeds (use as)
  • Superclass → subclass: downcast, may fail (use as? / as!)
  • Unrelated types: compile error S004

Choosing Between as and as! ​

FormFailureWhen to use
as?nilWhen the type is uncertain
as!panicWhen you are 100% sure
aspanicKnown at compile time

Complete Example ​

swift
class Shape { init() {} func area() -> Double { return 0 } }
class Circle: Shape {
    var r: Double
    init(r: Double) { self.r = r; super.init() }
    override func area() -> Double { return 3.14159 * r * r }
}
class Square: Shape {
    var s: Double
    init(s: Double) { self.s = s; super.init() }
    override func area() -> Double { return s * s }
}

func describe(_ s: Shape) -> String {
    if let c = s as? Circle {
        return "circle r=\(c.r), area=\(c.area())"
    }
    if let sq = s as? Square {
        return "square s=\(sq.s), area=\(sq.area())"
    }
    return "shape"
}

func main() {
    let shapes: Array<Shape> = [Circle(r: 2.0), Square(s: 3.0)]
    for s in shapes { print(describe(s)) }
}

Diagnostics ​

CodeTriggerFix
S004Incompatible conversion (including float→int, wrong suffix)Check the type relationship
S008Direct cross-actor access to instance fieldsGo through messaging / isolation APIs
S014Mixed signed/unsignedExplicit cast
S046is with a numeric targetUse an == comparison
R003as! failure; integer narrowing out of rangeChange to as? / keep the value in the target range
R007! unwrapping nilUse ?. / if let

Differences from Swift ​

SwiftSYX
is type checkImplemented (collections + scalars + classes)
as? / as! / asImplemented (runtime cast host)
Full cast protocol existentialPartially implemented (basic witness)
AnyObject / AnyWeakly supported (use Object / any P)
Generic erasurePartially implemented

Next chapter → Nested Types

See also → Language Reference: Types · Chapter 9, Enumerations · Chapter 25, Opaque Types

SYX · Apache-2.0