mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:30:12 +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
@@ -28,6 +28,7 @@ namespace Carbon {
|
||||
|
||||
class MixinPseudoType;
|
||||
class ConstraintType;
|
||||
class NominalClassType;
|
||||
|
||||
// Abstract base class of all AST nodes representing patterns.
|
||||
//
|
||||
@@ -275,9 +276,6 @@ class ClassDeclaration : public Declaration {
|
||||
auto type_params() -> std::optional<Nonnull<TuplePattern*>> {
|
||||
return type_params_;
|
||||
}
|
||||
auto base_expr() const -> std::optional<Nonnull<Expression*>> {
|
||||
return base_expr_;
|
||||
}
|
||||
auto self() const -> Nonnull<const SelfDeclaration*> { return self_decl_; }
|
||||
auto self() -> Nonnull<SelfDeclaration*> { return self_decl_; }
|
||||
|
||||
@@ -295,11 +293,18 @@ class ClassDeclaration : public Declaration {
|
||||
|
||||
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
|
||||
|
||||
auto base() const -> std::optional<Nonnull<const ClassDeclaration*>> {
|
||||
return base_;
|
||||
auto base_expr() const -> std::optional<Nonnull<Expression*>> {
|
||||
return base_expr_;
|
||||
}
|
||||
void set_base(Nonnull<const ClassDeclaration*> base_decl) {
|
||||
base_ = base_decl;
|
||||
|
||||
// Returns the original base type, before instantiation & substitutions
|
||||
// Use `NominalClassType::base()` to get the instantiated type.
|
||||
auto base_type() const -> std::optional<Nonnull<const NominalClassType*>> {
|
||||
return base_type_;
|
||||
}
|
||||
void set_base_type(
|
||||
std::optional<Nonnull<const NominalClassType*>> base_type) {
|
||||
base_type_ = base_type;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -311,6 +316,7 @@ class ClassDeclaration : public Declaration {
|
||||
std::vector<Nonnull<Declaration*>> members_;
|
||||
std::optional<Nonnull<FunctionDeclaration*>> destructor_;
|
||||
std::optional<Nonnull<const ClassDeclaration*>> base_;
|
||||
std::optional<Nonnull<const NominalClassType*>> base_type_;
|
||||
};
|
||||
|
||||
// EXPERIMENTAL MIXIN FEATURE
|
||||
|
||||
@@ -23,6 +23,7 @@ cc_library(
|
||||
":heap_allocation_interface",
|
||||
":stack",
|
||||
"//common:check",
|
||||
"//common:error",
|
||||
"//common:ostream",
|
||||
"//explorer/ast",
|
||||
"//explorer/common:arena",
|
||||
@@ -193,6 +194,7 @@ cc_library(
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "type_checker",
|
||||
srcs = [
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
@@ -426,40 +427,37 @@ auto TypeChecker::FieldTypesImplicitlyConvertible(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns all class members from class and its parent classes.
|
||||
static auto GetClassHierarchy(const NominalClassType& class_type)
|
||||
-> std::vector<Nonnull<const NominalClassType*>> {
|
||||
Nonnull<const NominalClassType*> curr_class_type = &class_type;
|
||||
std::vector<Nonnull<const NominalClassType*>> all_classes{curr_class_type};
|
||||
while (curr_class_type->base().has_value()) {
|
||||
curr_class_type = curr_class_type->base().value();
|
||||
all_classes.push_back(curr_class_type);
|
||||
}
|
||||
return all_classes;
|
||||
}
|
||||
|
||||
auto TypeChecker::FieldTypes(const NominalClassType& class_type) const
|
||||
-> std::vector<NamedValue> {
|
||||
std::vector<NamedValue> field_types;
|
||||
for (const auto class_type : GetClassHierarchy(class_type)) {
|
||||
for (Nonnull<Declaration*> m : class_type->declaration().members()) {
|
||||
switch (m->kind()) {
|
||||
case DeclarationKind::VariableDeclaration: {
|
||||
const auto& var = cast<VariableDeclaration>(*m);
|
||||
Nonnull<const Value*> field_type =
|
||||
Substitute(class_type->bindings(), &var.binding().static_type());
|
||||
field_types.push_back(
|
||||
{.name = var.binding().name(), .value = field_type});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
for (Nonnull<Declaration*> m : class_type.declaration().members()) {
|
||||
switch (m->kind()) {
|
||||
case DeclarationKind::VariableDeclaration: {
|
||||
const auto& var = cast<VariableDeclaration>(*m);
|
||||
Nonnull<const Value*> field_type =
|
||||
Substitute(class_type.bindings(), &var.binding().static_type());
|
||||
field_types.push_back(
|
||||
{.name = var.binding().name(), .value = field_type});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return field_types;
|
||||
}
|
||||
|
||||
auto TypeChecker::FieldTypesWithBase(const NominalClassType& class_type) const
|
||||
-> std::vector<NamedValue> {
|
||||
auto fields = FieldTypes(class_type);
|
||||
if (const auto base_type = class_type.base()) {
|
||||
auto base_fields = FieldTypesWithBase(*base_type.value());
|
||||
fields.emplace_back(NamedValue{std::string(NominalClassValue::BaseField),
|
||||
base_type.value()});
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
auto TypeChecker::IsImplicitlyConvertible(
|
||||
Nonnull<const Value*> source, Nonnull<const Value*> destination,
|
||||
const ImplScope& impl_scope, bool allow_user_defined_conversions) const
|
||||
@@ -484,10 +482,11 @@ auto TypeChecker::IsImplicitlyConvertible(
|
||||
}
|
||||
break;
|
||||
case Value::Kind::NominalClassType:
|
||||
if (FieldTypesImplicitlyConvertible(
|
||||
cast<StructType>(*source).fields(),
|
||||
FieldTypes(cast<NominalClassType>(*destination)),
|
||||
impl_scope)) {
|
||||
if (IsImplicitlyConvertible(
|
||||
source,
|
||||
arena_->New<StructType>(
|
||||
FieldTypesWithBase(cast<NominalClassType>(*destination))),
|
||||
impl_scope, allow_user_defined_conversions)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@@ -1897,10 +1896,15 @@ auto TypeChecker::SubstituteImpl(const Bindings& bindings,
|
||||
}
|
||||
case Value::Kind::NominalClassType: {
|
||||
const auto& class_type = cast<NominalClassType>(*type);
|
||||
auto base_type = class_type.base();
|
||||
if (base_type.has_value()) {
|
||||
base_type = cast<NominalClassType>(
|
||||
SubstituteImpl(base_type.value()->bindings(), base_type.value()));
|
||||
}
|
||||
Nonnull<const NominalClassType*> new_class_type =
|
||||
arena_->New<NominalClassType>(
|
||||
&class_type.declaration(),
|
||||
substitute_into_bindings(&class_type.bindings()));
|
||||
substitute_into_bindings(&class_type.bindings()), base_type);
|
||||
return new_class_type;
|
||||
}
|
||||
case Value::Kind::InterfaceType: {
|
||||
@@ -2567,9 +2571,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
const auto res,
|
||||
FindMemberWithParents(access.member_name(), &t_class));
|
||||
if (res.has_value()) {
|
||||
auto [member_type, member] = res.value();
|
||||
auto [member_type, member, member_t_class] = res.value();
|
||||
Nonnull<const Value*> field_type =
|
||||
Substitute(t_class.bindings(), member_type);
|
||||
Substitute(member_t_class->bindings(), member_type);
|
||||
access.set_member(Member(member));
|
||||
access.set_static_type(field_type);
|
||||
access.set_is_type_access(!IsInstanceMember(access.member()));
|
||||
@@ -4510,27 +4514,24 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
|
||||
Nonnull<Expression*> base_class_expr = *class_decl->base_expr();
|
||||
CARBON_ASSIGN_OR_RETURN(const auto base_type,
|
||||
TypeCheckTypeExp(base_class_expr, class_scope));
|
||||
switch (base_type->kind()) {
|
||||
case Value::Kind::NominalClassType:
|
||||
base_class = cast<NominalClassType>(base_type);
|
||||
if (base_class.value()->declaration().extensibility() ==
|
||||
ClassExtensibility::None) {
|
||||
return ProgramError(class_decl->source_loc())
|
||||
<< "Base class `" << base_class.value()->declaration().name()
|
||||
<< "` is `final` and cannot inherited. Add the `base` or "
|
||||
"`abstract` class prefix to `"
|
||||
<< base_class.value()->declaration().name()
|
||||
<< "` to allow it to be inherited";
|
||||
}
|
||||
class_decl->set_base(&base_class.value()->declaration());
|
||||
break;
|
||||
default:
|
||||
return ProgramError(class_decl->source_loc())
|
||||
<< "Unsupported base class type for class `"
|
||||
<< class_decl->name()
|
||||
<< "`. Only simple classes are currently supported as base "
|
||||
"class.";
|
||||
if (base_type->kind() != Value::Kind::NominalClassType) {
|
||||
return ProgramError(class_decl->source_loc())
|
||||
<< "Unsupported base class type for class `" << class_decl->name()
|
||||
<< "`. Only simple classes are currently supported as base "
|
||||
"class.";
|
||||
}
|
||||
|
||||
base_class = cast<NominalClassType>(base_type);
|
||||
if (base_class.value()->declaration().extensibility() ==
|
||||
ClassExtensibility::None) {
|
||||
return ProgramError(class_decl->source_loc())
|
||||
<< "Base class `" << base_class.value()->declaration().name()
|
||||
<< "` is `final` and cannot inherited. Add the `base` or "
|
||||
"`abstract` class prefix to `"
|
||||
<< base_class.value()->declaration().name()
|
||||
<< "` to allow it to be inherited";
|
||||
}
|
||||
class_decl->set_base_type(base_class);
|
||||
}
|
||||
|
||||
std::vector<Nonnull<const GenericBinding*>> bindings = scope_info.bindings;
|
||||
@@ -5599,17 +5600,18 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
|
||||
}
|
||||
|
||||
auto TypeChecker::FindMemberWithParents(
|
||||
std::string_view name, Nonnull<const NominalClassType*> class_type)
|
||||
std::string_view name, Nonnull<const NominalClassType*> enclosing_class)
|
||||
-> ErrorOr<std::optional<
|
||||
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>> {
|
||||
std::tuple<Nonnull<const Value*>, Nonnull<const Declaration*>,
|
||||
Nonnull<const NominalClassType*>>>> {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
const auto res,
|
||||
FindMixedMemberAndType(name, class_type->declaration().members(),
|
||||
class_type));
|
||||
FindMixedMemberAndType(name, enclosing_class->declaration().members(),
|
||||
enclosing_class));
|
||||
if (res.has_value()) {
|
||||
return res;
|
||||
return {std::make_tuple(res->first, res->second, enclosing_class)};
|
||||
}
|
||||
if (const auto base = class_type->base(); base.has_value()) {
|
||||
if (const auto base = enclosing_class->base(); base.has_value()) {
|
||||
return FindMemberWithParents(name, base.value());
|
||||
}
|
||||
return {std::nullopt};
|
||||
|
||||
@@ -60,12 +60,13 @@ class TypeChecker {
|
||||
SourceLocation source_loc) const
|
||||
-> std::optional<Nonnull<const Witness*>>;
|
||||
|
||||
// Return the declaration of the member with the given name, from the class
|
||||
// and its parents
|
||||
// Return the declaration of the member with the given name and the class type
|
||||
// that owns it, from the class and its parents
|
||||
auto FindMemberWithParents(std::string_view name,
|
||||
Nonnull<const NominalClassType*> enclosing_type)
|
||||
Nonnull<const NominalClassType*> enclosing_class)
|
||||
-> ErrorOr<std::optional<
|
||||
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>>;
|
||||
std::tuple<Nonnull<const Value*>, Nonnull<const Declaration*>,
|
||||
Nonnull<const NominalClassType*>>>>;
|
||||
|
||||
// Finds the direct or indirect member of a class or mixin by its name and
|
||||
// returns the member's declaration and type. Indirect members are members of
|
||||
@@ -345,6 +346,10 @@ class TypeChecker {
|
||||
auto FieldTypes(const NominalClassType& class_type) const
|
||||
-> std::vector<NamedValue>;
|
||||
|
||||
// Returns the field names and types of the class and its parents.
|
||||
auto FieldTypesWithBase(const NominalClassType& class_type) const
|
||||
-> std::vector<NamedValue>;
|
||||
|
||||
// Returns true if source_fields and destination_fields contain the same set
|
||||
// of names, and each value in source_fields is implicitly convertible to
|
||||
// the corresponding value in destination_fields. All values in both arguments
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -326,8 +326,14 @@ class StructValue : public Value {
|
||||
// A value of a nominal class type, i.e., an object.
|
||||
class NominalClassValue : public Value {
|
||||
public:
|
||||
NominalClassValue(Nonnull<const Value*> type, Nonnull<const Value*> inits)
|
||||
: Value(Kind::NominalClassValue), type_(type), inits_(inits) {}
|
||||
static constexpr llvm::StringLiteral BaseField{"base"};
|
||||
|
||||
NominalClassValue(Nonnull<const Value*> type, Nonnull<const Value*> inits,
|
||||
std::optional<Nonnull<const NominalClassValue*>> base)
|
||||
: Value(Kind::NominalClassValue),
|
||||
type_(type),
|
||||
inits_(inits),
|
||||
base_(base) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::NominalClassValue;
|
||||
@@ -335,10 +341,14 @@ class NominalClassValue : public Value {
|
||||
|
||||
auto type() const -> const Value& { return *type_; }
|
||||
auto inits() const -> const Value& { return *inits_; }
|
||||
auto base() const -> std::optional<Nonnull<const NominalClassValue*>> {
|
||||
return base_;
|
||||
}
|
||||
|
||||
private:
|
||||
Nonnull<const Value*> type_;
|
||||
Nonnull<const Value*> inits_; // The initializing StructValue.
|
||||
std::optional<Nonnull<const NominalClassValue*>> base_;
|
||||
};
|
||||
|
||||
// An alternative constructor value.
|
||||
@@ -649,7 +659,7 @@ class NominalClassType : public Value {
|
||||
explicit NominalClassType(
|
||||
Nonnull<const ClassDeclaration*> declaration,
|
||||
Nonnull<const Bindings*> bindings,
|
||||
std::optional<Nonnull<const NominalClassType*>> base = std::nullopt)
|
||||
std::optional<Nonnull<const NominalClassType*>> base)
|
||||
: Value(Kind::NominalClassType),
|
||||
declaration_(declaration),
|
||||
bindings_(bindings),
|
||||
|
||||
@@ -724,7 +724,9 @@ if_expression:
|
||||
expression:
|
||||
if_expression
|
||||
;
|
||||
designator: PERIOD identifier { $$ = $2; }
|
||||
designator:
|
||||
PERIOD identifier { $$ = $2; }
|
||||
| PERIOD BASE { $$ = "base"; }
|
||||
;
|
||||
paren_expression: paren_expression_base
|
||||
{ $$ = ExpressionFromParenContents(arena, context.source_loc(), $1); }
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// RUN: %{explorer-run}
|
||||
// RUN: %{explorer-run-trace}
|
||||
// CHECK:STDOUT: 1
|
||||
// CHECK:STDOUT: 2
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class A {
|
||||
fn Create() -> Self {
|
||||
return {.value_a = 1};
|
||||
}
|
||||
var value_a: i32;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
fn Create() -> Self {
|
||||
return {.base = A.Create(), .value_b = 2};
|
||||
}
|
||||
var value_b: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var b: B = B.Create();
|
||||
Print("{0}", b.value_a);
|
||||
Print("{0}", b.value_b);
|
||||
return 0;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ fn Main() -> i32 {
|
||||
// Note: D.FunctionC(0); is not accessible
|
||||
|
||||
// Calling function from class object
|
||||
var d: D = {};
|
||||
var d: D = {.base={}};
|
||||
d.FunctionC(1);
|
||||
d.FunctionD(2);
|
||||
return 0;
|
||||
|
||||
@@ -31,7 +31,7 @@ class D extends C {
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var d: D = {.value_c = 1, .value_d = 2};
|
||||
var d: D = {.base = {.value_c = 1}, .value_d = 2};
|
||||
d.Method1();
|
||||
d.Method2();
|
||||
return 0;
|
||||
|
||||
+13
-4
@@ -5,29 +5,38 @@
|
||||
// AUTOUPDATE
|
||||
// RUN: %{explorer-run}
|
||||
// RUN: %{explorer-run-trace}
|
||||
// CHECK:STDOUT: A
|
||||
// CHECK:STDOUT: c.a: 1
|
||||
// CHECK:STDOUT: c.b: 2
|
||||
// CHECK:STDOUT: c.c: 3
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class A {
|
||||
fn FunctionA() {}
|
||||
var var_a: i32;
|
||||
var a: i32;
|
||||
var aa: String;
|
||||
}
|
||||
|
||||
base class B extends A {
|
||||
fn FunctionB() {}
|
||||
var var_b: i32;
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
fn FunctionC() {}
|
||||
var var_c: i32;
|
||||
var c: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var c: C = {.var_a=1, .var_b=2, .var_c=3};
|
||||
var c: C = {.base={.base={.aa="A", .a=1}, .b=2, }, .c=3};
|
||||
c.FunctionA();
|
||||
c.FunctionB();
|
||||
c.FunctionC();
|
||||
Print(c.aa);
|
||||
Print("c.a: {0}", c.a);
|
||||
Print("c.b: {0}", c.b);
|
||||
Print("c.c: {0}", c.c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ class D extends C {
|
||||
|
||||
fn Main() -> i32 {
|
||||
// Initialize derived value first, base value second
|
||||
var d: D = {.value = 2, .value = 1};
|
||||
var d: D = {.value = 2, .base={.value = 1}};
|
||||
d.Method1();
|
||||
|
||||
Print("d.value = {0}", d.value);
|
||||
|
||||
@@ -23,7 +23,7 @@ class D extends C {
|
||||
|
||||
fn Main() -> i32 {
|
||||
// Initialization
|
||||
var d: D = {.var_c= 1, .var_d= 2};
|
||||
var d: D = {.base = {.var_c= 1}, .var_d= 2};
|
||||
|
||||
// Read
|
||||
Print("Read var_c={0}", d.var_c);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// RUN: %{not} %{explorer-run}
|
||||
// RUN: %{not} %{explorer-run-trace}
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class A {
|
||||
var a: i32;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_direct_base_class_init.carbon:[[@LINE+1]]: type error in name binding: '{.a: i32, .b: i32}' is not implicitly convertible to 'class B'
|
||||
var b: B = {.a=0, .b=1};
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// RUN: %{not} %{explorer-run}
|
||||
// RUN: %{not} %{explorer-run-trace}
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class Point {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
class Point3D extends Point {
|
||||
var z: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var p: Point3D = {.base={.x = 1, .y = 2}, .z = 3};
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_field_access_mismatch_base.carbon:[[@LINE+1]]: class Point3D does not have a field named a
|
||||
return p.a;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// RUN: %{not} %{explorer-run}
|
||||
// RUN: %{not} %{explorer-run-trace}
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class Point {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
class Point3D extends Point {
|
||||
var z: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var p: Point3D = {.base={.x = 1, .y = 2}, .z = 3};
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_field_assign_mismatch_base.carbon:[[@LINE+1]]: class Point3D does not have a field named a
|
||||
p.a = 0;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// RUN: %{explorer-run}
|
||||
// RUN: %{explorer-run-trace}
|
||||
// CHECK:STDOUT: 2
|
||||
// CHECK:STDOUT: 1
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface Number {
|
||||
fn Zero() -> Self;
|
||||
fn Add[me: Self](other: Self) -> Self;
|
||||
}
|
||||
|
||||
external impl i32 as Number {
|
||||
fn Zero() -> i32 { return 0; }
|
||||
fn Add[me: i32](other: i32) -> i32 { return me + other; }
|
||||
}
|
||||
|
||||
base class A(T:! Number) {
|
||||
var value_a: T;
|
||||
}
|
||||
|
||||
class B extends A(i32) {
|
||||
var value_b: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var b: B = {.base = {.value_a = 1}, .value_b = 2};
|
||||
Print("{0}", b.value_b);
|
||||
Print("{0}", b.value_a);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user