@@ -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
|
||||
@@ -15,8 +15,8 @@ Outer attributes apply to the next item:
|
||||
```mist
|
||||
#[derive(Debug, Clone)]
|
||||
struct Point {
|
||||
i32 x,
|
||||
i32 y,
|
||||
i32 x;
|
||||
i32 y;
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+27
-109
@@ -33,6 +33,29 @@ class Player
|
||||
}
|
||||
```
|
||||
|
||||
### Virtual Methods
|
||||
|
||||
The `virtual` keyword marks methods as dispatchable through the vtable, enabling polymorphic behavior:
|
||||
|
||||
```mist
|
||||
pub class Animal
|
||||
{
|
||||
str& name;
|
||||
|
||||
pub constructor(str& name)
|
||||
{
|
||||
self.name = name;
|
||||
}
|
||||
|
||||
pub virtual void speak(&self)
|
||||
{
|
||||
println!("...");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When a method is marked `virtual`, it can be overridden in subclasses and will be dispatched dynamically at runtime through the vtable. Non-virtual methods are called statically.
|
||||
|
||||
### Inheritance
|
||||
|
||||
```mist
|
||||
@@ -59,6 +82,8 @@ void speak(&self) override(Animal)
|
||||
}
|
||||
```
|
||||
|
||||
Methods marked `override` are automatically virtual if the parent method is virtual. The semantic analyzer validates that overridden methods match the parent's signature.
|
||||
|
||||
### Implementations
|
||||
|
||||
You can implement directly on the class body:
|
||||
@@ -78,113 +103,6 @@ class Circle
|
||||
}
|
||||
```
|
||||
|
||||
### 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.
|
||||
For details on how classes are compiled — vtable layout, constructor codegen, and safety verification — see [Class Internals](/docs/internals/classes).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Modules & Imports
|
||||
description: Module declarations, imports, re-exports, and module resolution rules.
|
||||
description: Module declarations, imports, re-exports, include directives, and module resolution rules.
|
||||
icon: FolderTree
|
||||
---
|
||||
|
||||
@@ -18,6 +18,26 @@ use std::collections::HashMap;
|
||||
pub use my_module::MyType;
|
||||
```
|
||||
|
||||
### Include Directives
|
||||
|
||||
Mist supports C-style include directives for
|
||||
|
||||
```mist
|
||||
// Global include (std::Display)
|
||||
// Includes all of the items *inside* the path
|
||||
#include <std/fmt>
|
||||
|
||||
// Use include (std::Display)
|
||||
// Includes *the path* itelf to the root.
|
||||
#use std::fmt::Display;
|
||||
|
||||
// Local include
|
||||
// Textual include, transpiles to rust include! macro via .rs extension
|
||||
#include "my_local_module.mist"
|
||||
```
|
||||
|
||||
Include directives allow you to bring in external code without using the module system, which is useful for C interop and local file organization.
|
||||
|
||||
### Module Resolution
|
||||
|
||||
Mist maps the module tree to Rust's module system:
|
||||
|
||||
@@ -9,13 +9,13 @@ icon: Box
|
||||
```mist
|
||||
pub struct Point
|
||||
{
|
||||
i32 x,
|
||||
i32 y,
|
||||
i32 x;
|
||||
i32 y;
|
||||
}
|
||||
|
||||
pub struct Generic<T>
|
||||
{
|
||||
T value,
|
||||
T value;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -24,8 +24,8 @@ Fields can be public or private:
|
||||
```mist
|
||||
struct User
|
||||
{
|
||||
pub str& name,
|
||||
i32 age, // private
|
||||
pub str& name;
|
||||
i32 age; // private
|
||||
}
|
||||
```
|
||||
|
||||
@@ -34,19 +34,19 @@ struct User
|
||||
```mist
|
||||
pub enum Option<T>
|
||||
{
|
||||
Some(T),
|
||||
None,
|
||||
Some(T);
|
||||
None;
|
||||
}
|
||||
|
||||
pub enum Message
|
||||
{
|
||||
Quit,
|
||||
Move(i32, i32),
|
||||
Write { str& content, i32 length },
|
||||
Quit;
|
||||
Move(i32, i32);
|
||||
Write { str& content; i32 length; };
|
||||
}
|
||||
```
|
||||
|
||||
Enum variants can be:
|
||||
- **Named** — `Variant`
|
||||
- **Tuple** — `Variant(T1, T2)`
|
||||
- **Struct** — `Variant { T1 field1, T2 field2 }`
|
||||
- **Struct** — `Variant { T1 field1; T2 field2; }`
|
||||
|
||||
@@ -19,13 +19,14 @@ true // Boolean
|
||||
false // Boolean
|
||||
"hello" // String
|
||||
(1, true, "x") // Tuple
|
||||
[1, 2, 3] // Array
|
||||
```
|
||||
|
||||
### Identifiers & Keywords
|
||||
|
||||
Keywords are reserved and cannot be used as identifiers:
|
||||
|
||||
`if`, `else`, `fn`, `for`, `while`, `match`, `return`, `break`, `continue`, `struct`, `enum`, `class`, `trait`, `impl`, `use`, `pub`, `mut`, `let`, `true`, `false`, `dyn`, `loop`, `unsafe`, `override`, `const`, `type`
|
||||
`if`, `else`, `fn`, `for`, `while`, `match`, `return`, `break`, `continue`, `struct`, `enum`, `class`, `trait`, `impl`, `use`, `pub`, `mut`, `let`, `true`, `false`, `dyn`, `loop`, `unsafe`, `override`, `const`, `type`, `virtual`
|
||||
|
||||
Identifiers follow the pattern `[a-zA-Z_][a-zA-Z0-9_]*`.
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ i32 'a& // Reference with lifetime
|
||||
i32 unsafe& // Const pointer
|
||||
i32 mut unsafe& // Mutable pointer
|
||||
(i32, bool) // Tuple type
|
||||
i32[]& // Array type (static non-fixed size)
|
||||
i32[10] // Array type (fixed size)
|
||||
bool fn(i32) // Function pointer type
|
||||
bool Fn(i32) // Closure trait (Fn)
|
||||
bool FnMut(i32) // Closure trait (FnMut)
|
||||
|
||||
@@ -5,14 +5,15 @@ icon: Variable
|
||||
---
|
||||
|
||||
```mist
|
||||
let x = 42; // Type-inferred immutable
|
||||
let mut y = 10; // Mutable variable
|
||||
i32 z = 100; // Explicit type annotation
|
||||
str& s = "hello"; // Typed string reference
|
||||
bool b = true; // Typed boolean
|
||||
f64 f = 3.14; // Typed float
|
||||
let (a, b) = (1, "two"); // Destructuring
|
||||
let (x, (y, z)) = (1, (2, 3)); // Nested destructuring
|
||||
let x = 42; // Type-inferred immutable
|
||||
let mut y = 10; // Mutable variable
|
||||
i32 z = 100; // Explicit type annotation
|
||||
str& s = "hello"; // Typed string reference
|
||||
bool b = true; // Typed boolean
|
||||
f64 f = 3.14; // Typed float
|
||||
(i32, str&) as x, y = (1, "hello"); // Typed tuple destructuring
|
||||
let (a, b) = (1, "two"); // Destructuring
|
||||
let (x, (y, z)) = (1, (2, 3)); // Nested destructuring
|
||||
```
|
||||
|
||||
Variable declarations follow either of two forms:
|
||||
@@ -22,6 +23,16 @@ let <pattern> [= <expr>];
|
||||
<type> <pattern> [= <expr>];
|
||||
```
|
||||
|
||||
### Tuple Variable Declarations
|
||||
|
||||
Tuple variables can be declared with explicit type annotations using `as`:
|
||||
|
||||
```mist
|
||||
(i32, bool) as x, y = (1, true);
|
||||
|
||||
i32 a, b = (1, 2, 3);
|
||||
```
|
||||
|
||||
### Const and Static
|
||||
|
||||
```mist
|
||||
|
||||
@@ -13,7 +13,7 @@ Mist is currently distributed as a Cargo crate. To get started, you'll need to h
|
||||
Run the following command to install the Mist toolchain:
|
||||
|
||||
```bash title="Terminal"
|
||||
cargo install mist-lang@0.3.8-alpha.0
|
||||
cargo install mist-lang@0.4.0
|
||||
```
|
||||
|
||||
> (ℹ) For a more streamlined development experience, Install the following VSCode extensions:
|
||||
|
||||
@@ -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.
|
||||
@@ -4,7 +4,7 @@ description: Understanding the Alpha state of Mist and our technical roadmap.
|
||||
icon: TriangleAlert
|
||||
---
|
||||
|
||||
Mist is currently in a **Volatile Alpha** stage (latest: v0.3.8-alpha.0). Our current priority is exploring **Syntax and Features**. We believe in stabilizing the developer experience and the "feel" of the language before locking in the deep architectural logic of the compiler.
|
||||
Mist is currently in a **Beta** stage (latest: v0.4.0). Our current priority is exploring **Syntax and Features**. We believe in stabilizing the developer experience and the "feel" of the language before locking in the deep architectural logic of the compiler.
|
||||
|
||||
The compiler, transpiler, and CLI are partially bootstrapped — written in Mist itself. This gives us real-world feedback on every language design decision.
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"internals/architecture",
|
||||
"internals/parsing",
|
||||
"internals/semantic-analysis",
|
||||
"internals/classes",
|
||||
"internals/codegen",
|
||||
"internals/transpiler",
|
||||
"internals/builder",
|
||||
|
||||
@@ -15,7 +15,7 @@ const faqs = [
|
||||
{
|
||||
question: "How do I install Mist?",
|
||||
answer:
|
||||
"Mist is distributed as a Cargo crate. Install it by running 'cargo install mist-lang@0.3.8-alpha.0'. You'll need the Rust toolchain installed. Once installed, create a new project with 'mist new my-project' and run it with 'mist run'. Check the Get Started guide for full details.",
|
||||
"Mist is distributed as a Cargo crate. Install it by running 'cargo install mist-lang@0.4.0'. You'll need the Rust toolchain installed. Once installed, create a new project with 'mist new my-project' and run it with 'mist run'. Check the Get Started guide for full details.",
|
||||
},
|
||||
{
|
||||
question: "Can I use Rust libraries with Mist?",
|
||||
@@ -40,7 +40,7 @@ const faqs = [
|
||||
{
|
||||
question: "Is Mist production-ready?",
|
||||
answer:
|
||||
"Mist is currently in a Volatile Alpha stage (latest: v0.3.8-alpha.0) and is not yet production-ready. The language is evolving rapidly — the current priority is exploring syntax and features before locking in deep architectural logic. You may encounter breaking changes between releases.",
|
||||
"Mist is currently in a Beta stage (latest: v0.4.0) and is not yet production-ready. The language is evolving rapidly — the current priority is exploring syntax and features before locking in deep architectural logic. You may encounter breaking changes between releases.",
|
||||
},
|
||||
{
|
||||
question: "How does the Mist compiler work?",
|
||||
|
||||
Reference in New Issue
Block a user