Hot++

Type design

This page records an approved design for the types of HotScript. It is a design note. The translator does not implement all of it yet.

Decisions

Type mapping table

HotScript C++ Rule
T (builtin, struct, native class) T A value.
T (script class) hot::Value<T> An owning box over the shell. See the notes below.
T (native class with a handle stub or a default_handle) The handle form of the stub. The stub sets the storage. $T names the same handle.
T* T* Borrowed. The analyzer treats it as nullable.
const T* const T* Only in casts.
$T The handle form of the type rule. The default is std::shared_ptr<T>. A shared owner. The analyzer treats it as nullable. A field with no initializer is null.
T*? and $T? none Not valid. T* and $T are already nullable.
T? std::optional<T> Value types only. It is an error on a pointer, a handle or a script class.
T[] hot::span<T> (or std::span<T> with HOT_VIEW_TYPE=std) A view. It is an error on a field and on a property. A local or a parameter is valid. A return of a local std.vector<T> or T[N] is an error, and so is a later use of a view after the source changed shape (see “Lifetime of a ref”). The index is checked. The owning list is the std prelude type std.vector<T>.
T[N] std::array<T, N> A fixed array.
[weak] $T The weak template of the rule. The default is std::weak_ptr<T>. A read emits hot::lock(w) and gives $T.
int int32_t  
long int64_t  
float float  
double double  
bool bool  
byte uint8_t  

Notes on hot::Value<T>:

Parameter defaults

HotScript C++
T x hot::In<T> x
in T x const T&
ref T x T&
out T x T&. The body assigns it on every path.
[byval] T x T
$T x const std::shared_ptr<T>&
[byval] $T x std::shared_ptr<T> by value
T* p T*
T[] xs hot::span<T> by value (std::span<T> with HOT_VIEW_TYPE=std)

Locals and ref

Lifetime of a ref

A ref names a place. A place has a root. The checker finds the root in the initializer. It removes parentheses, .name, [index] and a prefix *. The name that remains is the root.

Root Lifetime of the place
a local the block of the local
a parameter (plain, in, [byval]) the body
a ref or out parameter the place of the caller
this, a field the object
a module variable, a name that is not a local or a field the program
a ref local the lifetime of its own root
c[i], c.front(), c.back(), c.at(k) an element of c: valid until c changes its shape
o.F(a, b) where F returns ref an element of any field of o, with the shortest of: the root of o and the root of each ref argument
a literal, new, an operator result, a cast, ?., a string interpolation, a collection literal, any other call a temporary: no lifetime

Rules:

Fix: copy the value (var r = ...), or keep the owner alive with a handle ($T).

Views. A view local acts like a ref to an element of its source. This holds for a local that has the declared type T[] or std.string_view and an initializer that is a local owner (std.vector<T>, T[N], std.string) or a field of this.

A view differs from a ref: a view can cross a lambda or an await, and the checker does not follow it there (see doc/limits.md).

Note: the check sees direct mutation only. See doc/limits.md.

new forms

Script C++ Type
new T(args) on a struct or a native class T(args) T
new T(args) on a script class hot::Value<T>(T__new(args)) T
new $T(args) on a script class, std::shared_ptr handle H(T__new_shared(args)) $T
new $T(args) on a script class, other handle H(T__new(args)) $T
new $T(args) on a native class std::make_shared<T>(args) $T
new $S(args) on a struct std::make_shared<S>(args) $S

$T rules

Const inference (v1)

Nullability

Native declarations

Keyword set

Reserved and used: class, var, new, null, this, in, ref, out, virtual, override, extern, const (casts and stubs), return, if, else, while, for, foreach, break, continue, true, false, as, is.

The design removes no project keyword. These keywords stay: struct, enum, interface, using, async, await, abstract, public, private, protected, get, set, try, catch, finally, throw, switch, case, default, do, void, base, static and the rest.

Kept project mappings

Migration table

Old form New form Reason
[value] T T A bare script class is a value, so the attribute has no use. [value] T is an error.
T& x parameter ref T x. Use out T x when the body only assigns it. & is not a type suffix. The keyword shows the intent.
const T& x parameter in T x in is the read only form. const is inferred.
T x parameter that must stay a copy [byval] T x A plain parameter is read only and the translator picks the C++ form.
ref var r = x; ref r = x; ref replaces the pair of words.
var& r = x; and foreach (var& x in c) ref r = x; and foreach (ref x in c) Same reason: & is not a type suffix.
Weapon w = null; (a bare script class as a handle) $Weapon w; A bare script class is a value. $ marks the shared owner. A value is never null.
const on a local, a field or a script method Remove it. The translator infers const. A cast and a [native] stub still name it.
new T(args) as a handle on a script class new $T(args) new T(args) gives a value. new $T(args) gives a handle.
Old owning T[] std.vector<T> T[] is now a view (hot::span<T>, or std::span<T> with HOT_VIEW_TYPE=std). The default works with C++17.
A checked pointer in script text T*. It is borrowed and the analyzer treats it as nullable. The null analysis checks each access at compile time. A T* field of a native class with a weak source is still hot::CheckedPtr<T> in the C++ output.
--null-errors None. A null finding is an error by default. Use --null-warnings for warnings. The default is the strict form.
An extern block A [native] stub or a header import (using "x.h";) extern is a reserved word only. It has no syntax.
[native] stubs They stay. A stub gives the handle rule and the attributes.