Hot++

operator->, stub checks and index bounds

Types with operator->

. works on a value, a pointer, a reference and a std.shared_ptr. It does not work on an iterator, a std.optional or a std.unique_ptr. Write (*p).x for these types.

using "Actor.h";

class Holder : Actor {
    void Run(ref std.unique_ptr<Actor> p) {
        // p.Position.x does not compile
        float x = (*p).Position.x;
    }
}

Checks on a stub

The translator checks a stub against the native type.

Bounds check on an index

The script writes an index as a[i]. The generated C++ calls hot::index(a, i). A type that is checked gets a bounds test. An index that is negative or not less than size() throws IndexOutOfRange. Other types use their own operator[].

These types are checked by default:

Other types are not checked. For example std::map keeps its own operator[].

The stub attribute checked_index has no argument. It makes a native type checked:

using "Ring.h";

[native, checked_index] class my.Ring<*>;   // every my::Ring<T>
[native, checked_index] class my.Buffer;    // my::Buffer only

class Wave {
    my.Ring<float> samples;
    float Last() { return this.samples[this.samples.size() - 1]; }
}

The translator writes this for a stub in the module header:

namespace hot { template <class... A> struct checked_index< ::my::Ring<A...>> : std::true_type {}; }

A host can write the specialization by hand, for example for a type that a .cpp script uses. A .cpp script has raw bodies and calls operator[] itself. A hand-written std::false_type specialization removes the default check of a type. Do not combine a stub with a hand-written hot::checked_index specialization for the same type: the two definitions are a redefinition in one translation unit.

Put the checked_index stubs in a shared prelude. Then every module of one program writes the same trait definition. A module that uses the type without the stub can have another trait value. In an AOT program, the modules are compiled into one program. A module without the stub can then differ from a module with it, and the one-definition rule is broken for that type.

The macro HOT_RUNTIME_CHECKS=0 (CMake option HOT_RUNTIME_CHECKS=OFF) removes the test of every checked type. Then an index outside the container is undefined behavior again, and hot::index costs nothing. The trait hot::checked_index is separate from hot::holds_no_code. A checked type that is not trivially copyable still needs a holds_no_code specialization if it cannot hold code.