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
@@ -5,8 +5,11 @@
|
||||
#include "explorer/interpreter/value.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/error.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
@@ -33,6 +36,18 @@ auto StructValue::FindField(std::string_view name) const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static auto FindClassField(Nonnull<const NominalClassValue*> object,
|
||||
std::string_view name)
|
||||
-> std::optional<Nonnull<const Value*>> {
|
||||
if (auto field = cast<StructValue>(object->inits()).FindField(name)) {
|
||||
return field;
|
||||
}
|
||||
if (object->base().has_value()) {
|
||||
return FindClassField(object->base().value(), name);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const FieldPath::Component& field,
|
||||
SourceLocation source_loc, Nonnull<const Value*> me_value)
|
||||
@@ -89,7 +104,7 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
const auto& object = cast<NominalClassValue>(*v);
|
||||
// Look for a field.
|
||||
if (std::optional<Nonnull<const Value*>> field =
|
||||
cast<StructValue>(object.inits()).FindField(f)) {
|
||||
FindClassField(&object, f)) {
|
||||
return *field;
|
||||
} else {
|
||||
// Look for a method in the object's class
|
||||
@@ -176,10 +191,23 @@ static auto SetFieldImpl(
|
||||
}
|
||||
case Value::Kind::NominalClassValue: {
|
||||
const auto& object = cast<NominalClassValue>(*value);
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inits,
|
||||
SetFieldImpl(arena, &object.inits(), path_begin,
|
||||
path_end, field_value, source_loc));
|
||||
return arena->New<NominalClassValue>(&object.type(), inits);
|
||||
if (auto inits = SetFieldImpl(arena, &object.inits(), path_begin,
|
||||
path_end, field_value, source_loc);
|
||||
inits.ok()) {
|
||||
return arena->New<NominalClassValue>(&object.type(), *inits,
|
||||
object.base());
|
||||
} else if (object.base().has_value()) {
|
||||
auto new_base = SetFieldImpl(arena, object.base().value(), path_begin,
|
||||
path_end, field_value, source_loc);
|
||||
if (new_base.ok()) {
|
||||
return arena->New<NominalClassValue>(
|
||||
&object.type(), &object.inits(),
|
||||
cast<NominalClassValue>(*new_base));
|
||||
}
|
||||
}
|
||||
// Failed to match, show full object content
|
||||
return ProgramError(source_loc)
|
||||
<< "field " << (*path_begin).name() << " not in " << *value;
|
||||
}
|
||||
case Value::Kind::TupleType:
|
||||
case Value::Kind::TupleValue: {
|
||||
@@ -271,6 +299,9 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
case Value::Kind::NominalClassValue: {
|
||||
const auto& s = cast<NominalClassValue>(*this);
|
||||
out << cast<NominalClassType>(s.type()).declaration().name() << s.inits();
|
||||
if (s.base().has_value()) {
|
||||
out << " base " << *s.base().value();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Value::Kind::TupleType:
|
||||
@@ -1066,8 +1097,8 @@ auto FindFunctionWithParents(std::string_view name,
|
||||
if (auto fun = FindFunction(name, class_decl.members()); fun.has_value()) {
|
||||
return fun;
|
||||
}
|
||||
if (class_decl.base().has_value()) {
|
||||
return FindFunctionWithParents(name, *class_decl.base().value());
|
||||
if (const auto base_type = class_decl.base_type(); base_type.has_value()) {
|
||||
return FindFunctionWithParents(name, base_type.value()->declaration());
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user