Type System

AutoLang uses a hybrid type system combining static typing with dynamic behavior. Most types are validated at compile time, while Any acts as a universal container that can wrap any value.

Primitive Types

TypeDescription
Int64-bit signed integer (int64_t)
Float64-bit floating point (double)
BoolBoolean value (true or false)
StringSequence of characters
AnyUniversal container type

Primitive Conversions

To convert between primitive types, you can use syntax that looks like a function call: Int(), Float(). Under the hood, these are not actual functions. The compiler intercepts them as a CastNode and directly emits a dedicated bytecode instruction, meaning primitive conversions have zero function-call overhead.

// 1. Float to Int (Truncates the decimal part) val pi: Float = 3.14159 val approximatePi: Int = Int(pi) println(approximatePi) // Output: 3 // 2. Int to Float val score: Int = 100 val preciseScore: Float = Float(score) println(preciseScore) // Output: 100.0

The Any Container

Any is a special container type. It can wrap any value without changing the original type internally. It is not the root of the inheritance hierarchy.

val number: Int = 5 val text: String = "Hello" val a: Any = number val b: Any = text println(a) println(b)

Nullable Types

Nullable types are declared using ?. Nullable safety is enforced at compile time.

val a: Int? = null val b: Int = 5 // val c: Int = a // Compile-time error: Cannot assign nullable to non-nullable val d: Int = 10 println(d)

AutoLang does not provide smart casting. You must explicitly unwrap nullable values using !.

Type Checking (is)

The is operator checks the runtime type of a value.

val a: Any = 5 if (a is Int) { println("a is an Integer") } else { println("a is not an Integer") }

Unsafe Cast (as)

The as operator performs an explicit cast. It is allowed when:

  • There is an inheritance relationship
  • OR one of the types is Any

If the runtime type is incompatible, a runtime error is thrown. Casting to a nullable type using as is not allowed.

val a: Any = 20 val b = a as Int println(b * 2) // Output: 40

Safe Cast (as?)

The as? operator attempts a safe cast. If the cast fails, null is returned instead of throwing an error.

val a: Any = "Hello" val b = a as? Int if (b == null) { println("Cast failed gracefully") } else { println(b) }

Compile-Time Restrictions

If two types have no inheritance relationship and neither is Any, the compiler will reject the cast entirely at compile time.

val text: String = "Hello" // val number = text as Int // Compile-time error

Advanced: Runtime Type IDs

For deep integrations or building low-level data structures (like standard library Collections), Autolang provides a special compiler function: getClassId(Type).

  • Like magic constants (__FILE__), it is resolved entirely at compile time.
  • It returns the unique Int identifier of a class or primitive type.
val intTypeId = getClassId(Int) val stringTypeId = getClassId(String) println(intTypeId) println(stringTypeId) // You can pass these IDs directly to C++ via @native functions!

⚡ Want to see how this powers the compiler?

Tricks like getClassId() and CastNode are heavily used under the hood to completely eliminate runtime overhead and avoid expensive reflection.