Updated class examples and added blo
This commit is contained in:
@@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
title: Mist 0.4.0 is released, virtual methods, array types, and more
|
||||||
|
author: Klesti Selimaj
|
||||||
|
date: 2026-07-12
|
||||||
|
---
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
### Virtual Methods
|
||||||
|
|
||||||
|
The `virtual` keyword enables polymorphic dispatch through vtables. Methods marked `virtual` can be overridden in subclasses and will be dispatched dynamically at runtime:
|
||||||
|
|
||||||
|
```mist
|
||||||
|
pub class Animal
|
||||||
|
{
|
||||||
|
pub virtual void speak(&self)
|
||||||
|
{
|
||||||
|
println!("...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dog : Animal
|
||||||
|
{
|
||||||
|
void speak(&self) override
|
||||||
|
{
|
||||||
|
println!("Woof!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Array Types
|
||||||
|
|
||||||
|
Fixed-size array types are now supported with `[T; N]` syntax:
|
||||||
|
|
||||||
|
```mist
|
||||||
|
[i32; 10] numbers; // Array of 10 i32s
|
||||||
|
[str&; 3] names; // Array of 3 string references
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tuple Variable Declarations
|
||||||
|
|
||||||
|
Tuple variables can now be declared with explicit type annotations using `as`:
|
||||||
|
|
||||||
|
```mist
|
||||||
|
(i32, bool) as x, y = (1, true);
|
||||||
|
|
||||||
|
i32 a, b = (1, 2, 3);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Include Directives
|
||||||
|
|
||||||
|
C-style include directives for importing external files and crates:
|
||||||
|
|
||||||
|
```mist
|
||||||
|
#include <stdio.h> // Global include
|
||||||
|
pub use my_crate::module; // Use include
|
||||||
|
#include "local.mist" // Local include
|
||||||
|
```
|
||||||
|
|
||||||
|
## Syntax Changes
|
||||||
|
|
||||||
|
### Allman Style Struct/Enum Fields
|
||||||
|
|
||||||
|
Struct and enum fields now use semicolons instead of commas:
|
||||||
|
|
||||||
|
```mist
|
||||||
|
// Before (v0.3.x)
|
||||||
|
struct Point { i32 x, i32 y }
|
||||||
|
|
||||||
|
// After (v0.4.0)
|
||||||
|
struct Point { i32 x; i32 y; }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- Fixed default initializers in classes
|
||||||
|
- Fixed the mapping system for error remapping
|
||||||
|
- Fixed include manifest path resolution
|
||||||
|
- Fixed various analyzer bugs
|
||||||
|
- Fixed the publish command
|
||||||
|
- Fixed completion pollution in LSP
|
||||||
|
|
||||||
|
## Improvements
|
||||||
|
|
||||||
|
- Updated bootstrap infrastructure
|
||||||
|
- Migrated to core library for `c_void` and classes
|
||||||
|
- Removed unnecessary lifetime annotations
|
||||||
|
- Fixed generics and function type handling
|
||||||
@@ -103,113 +103,6 @@ class Circle
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Under the Hood
|
---
|
||||||
|
|
||||||
A class `Dog : Animal` generates:
|
For details on how classes are compiled — vtable layout, constructor codegen, and safety verification — see [Class Internals](/docs/internals/classes).
|
||||||
|
|
||||||
1. A Rust struct with a `_super: Animal` field (or `_vptr: &'static [*const c_void]` for root classes)
|
|
||||||
2. A vtable constant with function pointers for each public method
|
|
||||||
3. An `impl` block with `Deref<Target = Animal>` and `DerefMut`
|
|
||||||
4. Method trampolines (`__m_<name>`) that are dispatched through the vtable
|
|
||||||
5. A `new()` constructor that initializes via `MaybeUninit` and calls the user's `constructor(&mut self)`
|
|
||||||
|
|
||||||
The vtable is unified: parent entries are copied, overridden entries replace parent slots, and new methods are appended.
|
|
||||||
|
|
||||||
### Safety
|
|
||||||
|
|
||||||
Classes use `MaybeUninit::zeroed().assume_init()` inside the generated `new()` function — an inherently unsafe operation. This raises a natural question: **Are class constructors unsafe?**
|
|
||||||
|
|
||||||
The answer is **no**. The compiler statically verifies that every field is initialized before the constructor returns, eliminating the undefined behavior that raw `MaybeUninit` would normally carry.
|
|
||||||
|
|
||||||
#### Static Field Initialization Verification
|
|
||||||
|
|
||||||
When a class has a constructor, the semantic checker (`check_class_semantics`) collects every declared field and walks the constructor body to prove each one is written to:
|
|
||||||
|
|
||||||
1. **Direct assignment tracking** — Expressions like `self.field = value` are recognized as mutations of `field`. The analyzer checks for `=` and `->` operators whose left-hand side is a `self.field` path.
|
|
||||||
|
|
||||||
2. **`&mut self.field` tracking** — Taking a mutable reference to a field (`&mut self.field`) also counts as initializing it, since the reference can only be taken if the field is being set up.
|
|
||||||
|
|
||||||
3. **Transitive method calls** — If the constructor calls `self.helper()`, the analyzer follows into `helper`'s body and tracks which fields *it* initializes. This transitively propagates through nested calls:
|
|
||||||
|
|
||||||
```mist
|
|
||||||
class Player
|
|
||||||
{
|
|
||||||
str& name;
|
|
||||||
i32 health;
|
|
||||||
|
|
||||||
pub constructor(str& name)
|
|
||||||
{
|
|
||||||
self.name = name;
|
|
||||||
self.setup_health();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_health(&mut self)
|
|
||||||
{
|
|
||||||
self.health = 100; // Counts toward constructor's verification
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Branch intersection** — For `if`/`else`, `match`, and loops, fields must be initialized in **all** branches. If one branch initializes `x` but another does not, `x` is considered uninitialized. This ensures soundness regardless of the runtime path:
|
|
||||||
|
|
||||||
```mist
|
|
||||||
pub constructor(bool flag)
|
|
||||||
{
|
|
||||||
if flag {
|
|
||||||
self.health = 100;
|
|
||||||
} else {
|
|
||||||
self.health = 0;
|
|
||||||
}
|
|
||||||
// Both branches init health ✓
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Super initialization** — When a class inherits, the `_super` field is added to the required-field list. Any assignment to `super` counts:
|
|
||||||
|
|
||||||
```mist
|
|
||||||
pub constructor(str& name)
|
|
||||||
{
|
|
||||||
super = Super::new(name);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If any field is uninitialized after the full analysis, a compile-time error is reported with the field's exact source location:
|
|
||||||
|
|
||||||
```
|
|
||||||
class field `Player.health` is uninitialized
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Override Validation at Compile Time
|
|
||||||
|
|
||||||
When a method uses the `override` keyword, the codegen emits a hidden test function that verifies the Deref chain at compile time:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[allow(invalid_value)]
|
|
||||||
fn __test_vt() {
|
|
||||||
let this: &Self = &unsafe { std::mem::MaybeUninit::<Self>::zeroed().assume_init() };
|
|
||||||
let _: &Target = this; // Forces compiler to check Deref<Target = Target>
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This ensures `&Self` can always deref into the base class type. If the inheritance hierarchy is invalid, the Rust compiler rejects it.
|
|
||||||
|
|
||||||
#### VTable Safety
|
|
||||||
|
|
||||||
The `_vptr` (vtable pointer) is set *twice* during construction:
|
|
||||||
|
|
||||||
1. **Before** the constructor body runs — enabling virtual dispatch inside the constructor itself
|
|
||||||
2. **After** the constructor body — in case a base-class constructor ran and overwrote the pointer
|
|
||||||
|
|
||||||
This ensures that virtual method calls work correctly even during object construction, without exposing uninitialized memory through the vtable.
|
|
||||||
|
|
||||||
#### Summary
|
|
||||||
|
|
||||||
| Risk | Mitigation |
|
|
||||||
|------|-----------|
|
|
||||||
| Uninitialized fields via `MaybeUninit` | Static field-initialization verification rejects incomplete constructors |
|
|
||||||
| UB from reading uninitialized fields | Intersection analysis ensures all branches init the same fields |
|
|
||||||
| Invalid override signatures | Compile-time Deref test validates the inheritance chain |
|
|
||||||
| Vtable corruption during construction | `_vptr` is set before and after the constructor body |
|
|
||||||
| Unsafe code in generated constructors | `#[allow(invalid_value)]` is scoped to the generated `new()` only |
|
|
||||||
|
|
||||||
The `MaybeUninit` pattern is an implementation detail of the generated code — the Mist compiler proves soundness at the language level, so the user's constructor body is safe Mist code with no manual `unsafe` annotations required.
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ let <pattern> [= <expr>];
|
|||||||
Tuple variables can be declared with explicit type annotations using `as`:
|
Tuple variables can be declared with explicit type annotations using `as`:
|
||||||
|
|
||||||
```mist
|
```mist
|
||||||
(i32, bool) (x, y) as = (1, true);
|
(i32, bool) as x, y = (1, true);
|
||||||
|
|
||||||
i32 a, b = (1, 2, 3);
|
i32 a, b = (1, 2, 3);
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
---
|
||||||
|
title: Class Internals
|
||||||
|
description: How classes are compiled under the hood — vtable layout, code generation, and safety verification.
|
||||||
|
icon: Shield
|
||||||
|
---
|
||||||
|
|
||||||
|
This page covers the implementation details of Mist classes. You don't need to read this to use classes — it's here for curiosity and compiler contributors.
|
||||||
|
|
||||||
|
### Under the Hood
|
||||||
|
|
||||||
|
A class `Dog : Animal` generates:
|
||||||
|
|
||||||
|
1. A Rust struct with a `_super: Animal` field (or `_vptr: &'static [*const c_void]` for root classes)
|
||||||
|
2. A vtable constant with function pointers for each public method
|
||||||
|
3. An `impl` block with `Deref<Target = Animal>` and `DerefMut`
|
||||||
|
4. Method trampolines (`__m_<name>`) that are dispatched through the vtable
|
||||||
|
5. A `new()` constructor that initializes via `MaybeUninit` and calls the user's `constructor(&mut self)`
|
||||||
|
|
||||||
|
The vtable is unified: parent entries are copied, overridden entries replace parent slots, and new methods are appended.
|
||||||
|
|
||||||
|
### Safety
|
||||||
|
|
||||||
|
Classes use `MaybeUninit::zeroed().assume_init()` inside the generated `new()` function — an inherently unsafe operation. This raises a natural question: **Are class constructors unsafe?**
|
||||||
|
|
||||||
|
The answer is **no**. The compiler statically verifies that every field is initialized before the constructor returns, eliminating the undefined behavior that raw `MaybeUninit` would normally carry.
|
||||||
|
|
||||||
|
#### Static Field Initialization Verification
|
||||||
|
|
||||||
|
When a class has a constructor, the semantic checker (`check_class_semantics`) collects every declared field and walks the constructor body to prove each one is written to:
|
||||||
|
|
||||||
|
1. **Direct assignment tracking** — Expressions like `self.field = value` are recognized as mutations of `field`. The analyzer checks for `=` and `->` operators whose left-hand side is a `self.field` path.
|
||||||
|
|
||||||
|
2. **`&mut self.field` tracking** — Taking a mutable reference to a field (`&mut self.field`) also counts as initializing it, since the reference can only be taken if the field is being set up.
|
||||||
|
|
||||||
|
3. **Transitive method calls** — If the constructor calls `self.helper()`, the analyzer follows into `helper`'s body and tracks which fields *it* initializes. This transitively propagates through nested calls:
|
||||||
|
|
||||||
|
```mist
|
||||||
|
class Player
|
||||||
|
{
|
||||||
|
str& name;
|
||||||
|
i32 health;
|
||||||
|
|
||||||
|
pub constructor(str& name)
|
||||||
|
{
|
||||||
|
self.name = name;
|
||||||
|
self.setup_health();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_health(&mut self)
|
||||||
|
{
|
||||||
|
self.health = 100; // Counts toward constructor's verification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Branch intersection** — For `if`/`else`, `match`, and loops, fields must be initialized in **all** branches. If one branch initializes `x` but another does not, `x` is considered uninitialized. This ensures soundness regardless of the runtime path:
|
||||||
|
|
||||||
|
```mist
|
||||||
|
pub constructor(bool flag)
|
||||||
|
{
|
||||||
|
if flag {
|
||||||
|
self.health = 100;
|
||||||
|
} else {
|
||||||
|
self.health = 0;
|
||||||
|
}
|
||||||
|
// Both branches init health ✓
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Super initialization** — When a class inherits, the `_super` field is added to the required-field list. Any assignment to `super` counts:
|
||||||
|
|
||||||
|
```mist
|
||||||
|
pub constructor(str& name)
|
||||||
|
{
|
||||||
|
super = Super::new(name);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If any field is uninitialized after the full analysis, a compile-time error is reported with the field's exact source location:
|
||||||
|
|
||||||
|
```
|
||||||
|
class field `Player.health` is uninitialized
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Override Validation at Compile Time
|
||||||
|
|
||||||
|
When a method uses the `override` keyword, the codegen emits a hidden test function that verifies the Deref chain at compile time:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[allow(invalid_value)]
|
||||||
|
fn __test_vt() {
|
||||||
|
let this: &Self = &unsafe { std::mem::MaybeUninit::<Self>::zeroed().assume_init() };
|
||||||
|
let _: &Target = this; // Forces compiler to check Deref<Target = Target>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures `&Self` can always deref into the base class type. If the inheritance hierarchy is invalid, the Rust compiler rejects it.
|
||||||
|
|
||||||
|
#### VTable Safety
|
||||||
|
|
||||||
|
The `_vptr` (vtable pointer) is set *twice* during construction:
|
||||||
|
|
||||||
|
1. **Before** the constructor body runs — enabling virtual dispatch inside the constructor itself
|
||||||
|
2. **After** the constructor body — in case a base-class constructor ran and overwrote the pointer
|
||||||
|
|
||||||
|
This ensures that virtual method calls work correctly even during object construction, without exposing uninitialized memory through the vtable.
|
||||||
|
|
||||||
|
#### Summary
|
||||||
|
|
||||||
|
| Risk | Mitigation |
|
||||||
|
|------|-----------|
|
||||||
|
| Uninitialized fields via `MaybeUninit` | Static field-initialization verification rejects incomplete constructors |
|
||||||
|
| UB from reading uninitialized fields | Intersection analysis ensures all branches init the same fields |
|
||||||
|
| Invalid override signatures | Compile-time Deref test validates the inheritance chain |
|
||||||
|
| Vtable corruption during construction | `_vptr` is set before and after the constructor body |
|
||||||
|
| Unsafe code in generated constructors | `#[allow(invalid_value)]` is scoped to the generated `new()` only |
|
||||||
|
|
||||||
|
The `MaybeUninit` pattern is an implementation detail of the generated code — the Mist compiler proves soundness at the language level, so the user's constructor body is safe Mist code with no manual `unsafe` annotations required.
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
"internals/architecture",
|
"internals/architecture",
|
||||||
"internals/parsing",
|
"internals/parsing",
|
||||||
"internals/semantic-analysis",
|
"internals/semantic-analysis",
|
||||||
|
"internals/classes",
|
||||||
"internals/codegen",
|
"internals/codegen",
|
||||||
"internals/transpiler",
|
"internals/transpiler",
|
||||||
"internals/builder",
|
"internals/builder",
|
||||||
|
|||||||
Reference in New Issue
Block a user