Class constructors initialize the inheritance chain from the direct base class
to the most-derived class. A derived constructor can pass arguments to its
direct base constructor with a contextual base(...) call:
class ValueBase {
public int32 value;
public ValueBase(int32 initial) {
value = initial;
}
}
class AdjustedValue : ValueBase {
public AdjustedValue(int32 initial) {
base(initial + 1);
value += 1;
}
}
base(...) must be the first statement in a class constructor. It is consumed
as constructor metadata rather than emitted as an ordinary function call. It
is rejected in functions, struct constructors, classes without a base class,
and later positions in a constructor body.
If a derived constructor does not contain an explicit base call, the compiler
inserts base() before its body. This is valid only when the direct base
constructor takes no arguments. A required-argument base constructor produces a
semantic error until the derived constructor calls it explicitly with the
correct argument count and types.
When a derived class declares no constructor, the compiler synthesizes a zero-argument constructor if any class in its base chain needs construction. This makes the automatic chain recursive:
class Root {
public Root() {
println("root");
}
}
class Middle : Root {
}
class Leaf : Middle {
}
Constructing Leaf calls Root(), then the synthetic Middle() body, and
finally the synthetic Leaf() body. Base-constructor failures use the normal
exception propagation path, so the derived body does not run after a failure.
Classes and structs may declare multiple constructors with distinct parameter
type lists (same rules as method overloads). Calls select the best applicable
constructor; ambiguity and no-match are semantic errors
(E_AMBIGUOUS_CONSTRUCTOR, E_NO_MATCHING_CONSTRUCTOR).
class Point {
public int32 x;
public int32 y;
public Point() { x = 0; y = 0; }
public Point(int32 v) { x = v; y = v; }
public Point(int32 px, int32 py) { x = px; y = py; }
}
Point* a = new Point();
Point* b = new Point(3);
Point* c = new Point(1, 2);
base(...) in a derived constructor participates in the same overload
resolution against the direct base class constructors. Implicit base() still
requires a zero-argument (or synthetic) base constructor.
LLVM symbols are mangled with parameter types via the same CallableKey
scheme as methods (Type.__ctor, Type.__ctor$int32, …).