mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Explorer: support .base to initialize parent class from struct (#2361)
Relates to https://github.com/carbon-language/carbon-lang/issues/1881 - Add support for `.base` field in structs for [parent class initialization](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#constructors) - Disabling base class initialization without `.base` - Support class constructors (`Create() -> Self`) for base classes - Direct access to base class(es) attributes with `object.var` remains unaffected Changes: - Add `TypeChecker::FieldTypesWithBase` to help assessing if a struct with `base` fields can be converted to a class - Add a new `base_type()` attribute+getter to `NominalClassDeclaration` to as a first step to allow resolving parametrized classes - Add a new `base` attribute+getter to `NominalClassValue` that contains the base class `NominalClassValue`. It is currently used mainly to get and set members of a class object. - Add `Interpreter::ConvertClassWithBase` to build `NominalClassValue` from a init struct, that contains `.base` fields with either `NominalClassValue` or `StructValue` - Add `FindClassField` to find a field in a class or its base classes - Remove superfluous `ClassDeclaration::base()` in favor of `ClassDeclaration::base_type()` Limitations; - Though some work is done in that direction, parametrized base class where time is not know at the declaration site are not supported. Namely the example below does not compile ``` base class A(T:! Type) {} class B(T:! Type) extends A(T) {} ``` But this one is functional already ``` base class A(T:! Type) {} class B extends A(i32) {} ``` Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
co-authored by
Richard Smith
parent
84deb62aef
commit
46f4887cf7
@@ -22,6 +22,7 @@
|
||||
#include "explorer/interpreter/action.h"
|
||||
#include "explorer/interpreter/action_stack.h"
|
||||
#include "explorer/interpreter/stack.h"
|
||||
#include "explorer/interpreter/value.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
@@ -104,6 +105,12 @@ class Interpreter {
|
||||
Nonnull<const Value*> destination_type,
|
||||
SourceLocation source_loc) -> ErrorOr<Nonnull<const Value*>>;
|
||||
|
||||
// Create a class value and its base class(es) from an init struct.
|
||||
auto ConvertStructToClass(Nonnull<const StructValue*> init,
|
||||
Nonnull<const NominalClassType*> class_type,
|
||||
SourceLocation source_loc)
|
||||
-> ErrorOr<Nonnull<NominalClassValue*>>;
|
||||
|
||||
// Evaluate an expression immediately, recursively, and return its result.
|
||||
//
|
||||
// TODO: Stop using this.
|
||||
@@ -610,10 +617,17 @@ auto Interpreter::InstantiateType(Nonnull<const Value*> type,
|
||||
}
|
||||
case Value::Kind::NominalClassType: {
|
||||
const auto& class_type = cast<NominalClassType>(*type);
|
||||
std::optional<Nonnull<const NominalClassType*>> base = class_type.base();
|
||||
if (base.has_value()) {
|
||||
CARBON_ASSIGN_OR_RETURN(const auto inst_base,
|
||||
InstantiateType(base.value(), source_loc));
|
||||
base = cast<NominalClassType>(inst_base);
|
||||
}
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Bindings*> bindings,
|
||||
InstantiateBindings(&class_type.bindings(), source_loc));
|
||||
return arena_->New<NominalClassType>(&class_type.declaration(), bindings);
|
||||
return arena_->New<NominalClassType>(&class_type.declaration(), bindings,
|
||||
base);
|
||||
}
|
||||
case Value::Kind::ChoiceType: {
|
||||
const auto& choice_type = cast<ChoiceType>(*type);
|
||||
@@ -661,6 +675,35 @@ auto Interpreter::InstantiateWitness(Nonnull<const Witness*> witness)
|
||||
return cast<Witness>(value);
|
||||
}
|
||||
|
||||
auto Interpreter::ConvertStructToClass(
|
||||
Nonnull<const StructValue*> init_struct,
|
||||
Nonnull<const NominalClassType*> class_type, SourceLocation source_loc)
|
||||
-> ErrorOr<Nonnull<NominalClassValue*>> {
|
||||
std::vector<NamedValue> struct_values;
|
||||
std::optional<Nonnull<const NominalClassValue*>> base_instance;
|
||||
// Instantiate the `destination_type` to obtain the runtime
|
||||
// type of the object.
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inst_class,
|
||||
InstantiateType(class_type, source_loc));
|
||||
for (const auto& field : init_struct->elements()) {
|
||||
if (field.name == NominalClassValue::BaseField) {
|
||||
CARBON_CHECK(class_type->base().has_value())
|
||||
<< "Invalid 'base' field for class '"
|
||||
<< class_type->declaration().name() << "' without base class.";
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
auto base,
|
||||
Convert(field.value, class_type->base().value(), source_loc));
|
||||
base_instance = cast<NominalClassValue>(base);
|
||||
} else {
|
||||
struct_values.push_back(field);
|
||||
}
|
||||
}
|
||||
auto* converted_init_struct =
|
||||
arena_->New<StructValue>(std::move(struct_values));
|
||||
return arena_->New<NominalClassValue>(inst_class, converted_init_struct,
|
||||
base_instance);
|
||||
}
|
||||
|
||||
auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
Nonnull<const Value*> destination_type,
|
||||
SourceLocation source_loc)
|
||||
@@ -730,12 +773,12 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
return arena_->New<StructValue>(std::move(new_elements));
|
||||
}
|
||||
case Value::Kind::NominalClassType: {
|
||||
// Instantiate the `destination_type` to obtain the runtime
|
||||
// type of the object.
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> inst_dest,
|
||||
InstantiateType(destination_type, source_loc));
|
||||
return arena_->New<NominalClassValue>(inst_dest, value);
|
||||
auto class_value,
|
||||
ConvertStructToClass(cast<StructValue>(value),
|
||||
cast<NominalClassType>(destination_type),
|
||||
source_loc));
|
||||
return class_value;
|
||||
}
|
||||
case Value::Kind::TypeType:
|
||||
case Value::Kind::ConstraintType:
|
||||
@@ -962,9 +1005,11 @@ auto Interpreter::CallFunction(const CallExpression& call,
|
||||
Nonnull<const Bindings*> bindings =
|
||||
arena_->New<Bindings>(std::move(generic_args), std::move(witnesses));
|
||||
switch (decl.kind()) {
|
||||
case DeclarationKind::ClassDeclaration:
|
||||
case DeclarationKind::ClassDeclaration: {
|
||||
const auto& class_decl = cast<ClassDeclaration>(decl);
|
||||
return todo_.FinishAction(arena_->New<NominalClassType>(
|
||||
&cast<ClassDeclaration>(decl), bindings));
|
||||
&class_decl, bindings, class_decl.base_type()));
|
||||
}
|
||||
case DeclarationKind::InterfaceDeclaration:
|
||||
return todo_.FinishAction(arena_->New<InterfaceType>(
|
||||
&cast<InterfaceDeclaration>(decl), bindings));
|
||||
|
||||
Reference in New Issue
Block a user