mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-01 12:45:53 +01:00
Explorer: Support class extension (#1946)
Support class extension with `extends` Relates to #1881 Add support for: * Class extension with `extends` * Using base class methods and members
This commit is contained in:
@@ -253,14 +253,14 @@ class ClassDeclaration : public Declaration {
|
||||
Nonnull<SelfDeclaration*> self_decl,
|
||||
ClassExtensibility extensibility,
|
||||
std::optional<Nonnull<TuplePattern*>> type_params,
|
||||
std::optional<Nonnull<Expression*>> extends,
|
||||
std::optional<Nonnull<Expression*>> base,
|
||||
std::vector<Nonnull<Declaration*>> members)
|
||||
: Declaration(AstNodeKind::ClassDeclaration, source_loc),
|
||||
name_(std::move(name)),
|
||||
extensibility_(extensibility),
|
||||
self_decl_(self_decl),
|
||||
type_params_(type_params),
|
||||
extends_(extends),
|
||||
base_expr_(base),
|
||||
members_(std::move(members)) {}
|
||||
|
||||
static auto classof(const AstNode* node) -> bool {
|
||||
@@ -275,8 +275,8 @@ class ClassDeclaration : public Declaration {
|
||||
auto type_params() -> std::optional<Nonnull<TuplePattern*>> {
|
||||
return type_params_;
|
||||
}
|
||||
auto extends() const -> std::optional<Nonnull<Expression*>> {
|
||||
return extends_;
|
||||
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,14 +295,22 @@ class ClassDeclaration : public Declaration {
|
||||
|
||||
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
|
||||
|
||||
auto base() const -> std::optional<Nonnull<const ClassDeclaration*>> {
|
||||
return base_;
|
||||
}
|
||||
void set_base(Nonnull<const ClassDeclaration*> base_decl) {
|
||||
base_ = base_decl;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
ClassExtensibility extensibility_;
|
||||
Nonnull<SelfDeclaration*> self_decl_;
|
||||
std::optional<Nonnull<TuplePattern*>> type_params_;
|
||||
std::optional<Nonnull<Expression*>> extends_;
|
||||
std::optional<Nonnull<Expression*>> base_expr_;
|
||||
std::vector<Nonnull<Declaration*>> members_;
|
||||
std::optional<Nonnull<FunctionDeclaration*>> destructor_;
|
||||
std::optional<Nonnull<const ClassDeclaration*>> base_;
|
||||
};
|
||||
|
||||
// EXPERIMENTAL MIXIN FEATURE
|
||||
|
||||
@@ -577,6 +577,10 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope,
|
||||
StaticScope class_scope;
|
||||
class_scope.AddParent(&enclosing_scope);
|
||||
enclosing_scope.MarkDeclared(class_decl.name());
|
||||
if (class_decl.base_expr().has_value()) {
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
ResolveNames(**class_decl.base_expr(), class_scope));
|
||||
}
|
||||
if (class_decl.type_params().has_value()) {
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
ResolveNames(**class_decl.type_params(), class_scope));
|
||||
|
||||
@@ -7,14 +7,21 @@
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/ostream.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
#include "explorer/common/nonnull.h"
|
||||
#include "explorer/common/source_location.h"
|
||||
#include "explorer/interpreter/impl_scope.h"
|
||||
#include "explorer/interpreter/interpreter.h"
|
||||
#include "explorer/interpreter/pattern_analysis.h"
|
||||
@@ -366,21 +373,35 @@ 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 (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;
|
||||
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;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return field_types;
|
||||
@@ -2194,11 +2215,10 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
case Value::Kind::NominalClassType: {
|
||||
const auto& t_class = cast<NominalClassType>(object_type);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
auto type_member, FindMixedMemberAndType(
|
||||
access.source_loc(), access.member_name(),
|
||||
t_class.declaration().members(), &t_class));
|
||||
if (type_member.has_value()) {
|
||||
auto [member_type, member] = type_member.value();
|
||||
const auto res,
|
||||
FindMemberWithParents(access.member_name(), &t_class));
|
||||
if (res.has_value()) {
|
||||
auto [member_type, member] = res.value();
|
||||
Nonnull<const Value*> field_type =
|
||||
Substitute(t_class.bindings(), member_type);
|
||||
access.set_member(Member(member));
|
||||
@@ -2413,9 +2433,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
const auto& class_type = cast<NominalClassType>(*type);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
auto type_member,
|
||||
FindMixedMemberAndType(
|
||||
access.source_loc(), access.member_name(),
|
||||
class_type.declaration().members(), &class_type));
|
||||
FindMixedMemberAndType(access.member_name(),
|
||||
class_type.declaration().members(),
|
||||
&class_type));
|
||||
if (type_member.has_value()) {
|
||||
auto [member_type, member] = type_member.value();
|
||||
access.set_member(Member(member));
|
||||
@@ -4067,13 +4087,37 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
|
||||
ImplScope class_scope;
|
||||
class_scope.AddParent(scope_info.innermost_scope);
|
||||
|
||||
if (class_decl->extensibility() != ClassExtensibility::None) {
|
||||
if (class_decl->extensibility() == ClassExtensibility::Abstract) {
|
||||
return ProgramError(class_decl->source_loc())
|
||||
<< "Class prefixes `base` and `abstract` are not supported yet";
|
||||
<< "Class prefix `abstract` is not supported yet";
|
||||
}
|
||||
if (class_decl->extends()) {
|
||||
return ProgramError(class_decl->source_loc())
|
||||
<< "Class extension with `extends` is not supported yet";
|
||||
|
||||
std::optional<Nonnull<const NominalClassType*>> base_class;
|
||||
if (class_decl->base_expr().has_value()) {
|
||||
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.";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Nonnull<const GenericBinding*>> bindings = scope_info.bindings;
|
||||
@@ -4090,7 +4134,7 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
|
||||
// For class declaration `class MyType(T:! Type, U:! AnInterface)`, `Self`
|
||||
// should have the value `MyType(T, U)`.
|
||||
Nonnull<NominalClassType*> self_type = arena_->New<NominalClassType>(
|
||||
class_decl, Bindings::SymbolicIdentity(arena_, bindings));
|
||||
class_decl, Bindings::SymbolicIdentity(arena_, bindings), base_class);
|
||||
self->set_static_type(arena_->New<TypeType>());
|
||||
self->set_constant_value(self_type);
|
||||
|
||||
@@ -5079,9 +5123,25 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
|
||||
return Success();
|
||||
}
|
||||
|
||||
auto TypeChecker::FindMemberWithParents(
|
||||
std::string_view name, Nonnull<const NominalClassType*> class_type)
|
||||
-> ErrorOr<std::optional<
|
||||
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>> {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
const auto res,
|
||||
FindMixedMemberAndType(name, class_type->declaration().members(),
|
||||
class_type));
|
||||
if (res.has_value()) {
|
||||
return res;
|
||||
}
|
||||
if (const auto base = class_type->base(); base.has_value()) {
|
||||
return FindMemberWithParents(name, base.value());
|
||||
}
|
||||
return {std::nullopt};
|
||||
}
|
||||
|
||||
auto TypeChecker::FindMixedMemberAndType(
|
||||
SourceLocation source_loc, const std::string_view& name,
|
||||
llvm::ArrayRef<Nonnull<Declaration*>> members,
|
||||
const std::string_view& name, llvm::ArrayRef<Nonnull<Declaration*>> members,
|
||||
const Nonnull<const Value*> enclosing_type)
|
||||
-> ErrorOr<std::optional<
|
||||
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>> {
|
||||
@@ -5091,8 +5151,7 @@ auto TypeChecker::FindMixedMemberAndType(
|
||||
Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
const auto res,
|
||||
FindMixedMemberAndType(source_loc, name,
|
||||
mixin->declaration().members(), mixin));
|
||||
FindMixedMemberAndType(name, mixin->declaration().members(), mixin));
|
||||
if (res.has_value()) {
|
||||
if (isa<NominalClassType>(enclosing_type)) {
|
||||
Bindings temp_map;
|
||||
|
||||
@@ -60,14 +60,20 @@ 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
|
||||
auto FindMemberWithParents(std::string_view name,
|
||||
Nonnull<const NominalClassType*> enclosing_type)
|
||||
-> ErrorOr<std::optional<
|
||||
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>>;
|
||||
|
||||
// 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
|
||||
// mixins that are mixed by member mix declarations. If the member is an
|
||||
// indirect member from a mix declaration, then the Self type variable within
|
||||
// the member's type is substituted with the type of the enclosing declaration
|
||||
// containing the mix declaration.
|
||||
auto FindMixedMemberAndType(SourceLocation source_loc,
|
||||
const std::string_view& name,
|
||||
auto FindMixedMemberAndType(const std::string_view& name,
|
||||
llvm::ArrayRef<Nonnull<Declaration*>> members,
|
||||
Nonnull<const Value*> enclosing_type)
|
||||
-> ErrorOr<std::optional<
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
#include "explorer/interpreter/action.h"
|
||||
@@ -98,7 +99,7 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
// Look for a method in the object's class
|
||||
const auto& class_type = cast<NominalClassType>(object.type());
|
||||
std::optional<Nonnull<const FunctionValue*>> func =
|
||||
class_type.FindFunction(f);
|
||||
FindFunctionWithParents(f, class_type.declaration());
|
||||
if (!func) {
|
||||
return ProgramError(source_loc) << "member " << f << " not in " << *v
|
||||
<< " or its " << class_type;
|
||||
@@ -127,7 +128,7 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
// Access a class function.
|
||||
const auto& class_type = cast<NominalClassType>(*v);
|
||||
std::optional<Nonnull<const FunctionValue*>> fun =
|
||||
class_type.FindFunction(f);
|
||||
FindFunctionWithParents(f, class_type.declaration());
|
||||
if (fun == std::nullopt) {
|
||||
return ProgramError(source_loc)
|
||||
<< "class function " << f << " not in " << *v;
|
||||
@@ -979,9 +980,10 @@ auto ChoiceType::FindAlternative(std::string_view name) const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto NominalClassType::FindFunction(std::string_view name) const
|
||||
auto FindFunction(std::string_view name,
|
||||
llvm::ArrayRef<Nonnull<Declaration*>> members)
|
||||
-> std::optional<Nonnull<const FunctionValue*>> {
|
||||
for (const auto& member : declaration().members()) {
|
||||
for (const auto& member : members) {
|
||||
switch (member->kind()) {
|
||||
case DeclarationKind::MixDeclaration: {
|
||||
const auto& mix_decl = cast<MixDeclaration>(*member);
|
||||
@@ -1034,6 +1036,18 @@ auto MixinPseudoType::FindFunction(const std::string_view& name) const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto FindFunctionWithParents(std::string_view name,
|
||||
const ClassDeclaration& class_decl)
|
||||
-> std::optional<Nonnull<const FunctionValue*>> {
|
||||
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());
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto FindMember(std::string_view name,
|
||||
llvm::ArrayRef<Nonnull<Declaration*>> members)
|
||||
-> std::optional<Nonnull<const Declaration*>> {
|
||||
|
||||
@@ -620,11 +620,14 @@ class NominalClassType : public Value {
|
||||
|
||||
// Construct a fully instantiated generic class type to represent the
|
||||
// run-time type of an object.
|
||||
explicit NominalClassType(Nonnull<const ClassDeclaration*> declaration,
|
||||
Nonnull<const Bindings*> bindings)
|
||||
explicit NominalClassType(
|
||||
Nonnull<const ClassDeclaration*> declaration,
|
||||
Nonnull<const Bindings*> bindings,
|
||||
std::optional<Nonnull<const NominalClassType*>> base = std::nullopt)
|
||||
: Value(Kind::NominalClassType),
|
||||
declaration_(declaration),
|
||||
bindings_(bindings) {}
|
||||
bindings_(bindings),
|
||||
base_(base) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::NominalClassType;
|
||||
@@ -634,6 +637,10 @@ class NominalClassType : public Value {
|
||||
|
||||
auto bindings() const -> const Bindings& { return *bindings_; }
|
||||
|
||||
auto base() const -> std::optional<Nonnull<const NominalClassType*>> {
|
||||
return base_;
|
||||
}
|
||||
|
||||
auto type_args() const -> const BindingMap& { return bindings_->args(); }
|
||||
|
||||
// Witnesses for each of the class's impl bindings.
|
||||
@@ -647,14 +654,10 @@ class NominalClassType : public Value {
|
||||
return declaration_->type_params().has_value() && type_args().empty();
|
||||
}
|
||||
|
||||
// Returns the value of the function named `name` in this class, or
|
||||
// nullopt if there is no such function.
|
||||
auto FindFunction(std::string_view name) const
|
||||
-> std::optional<Nonnull<const FunctionValue*>>;
|
||||
|
||||
private:
|
||||
Nonnull<const ClassDeclaration*> declaration_;
|
||||
Nonnull<const Bindings*> bindings_ = Bindings::None();
|
||||
std::optional<Nonnull<const NominalClassType*>> base_;
|
||||
};
|
||||
|
||||
class MixinPseudoType : public Value {
|
||||
@@ -692,6 +695,18 @@ class MixinPseudoType : public Value {
|
||||
Nonnull<const Bindings*> bindings_ = Bindings::None();
|
||||
};
|
||||
|
||||
// Returns the value of the function named `name` in this class, or
|
||||
// nullopt if there is no such function.
|
||||
auto FindFunction(std::string_view name,
|
||||
llvm::ArrayRef<Nonnull<Declaration*>> members)
|
||||
-> std::optional<Nonnull<const FunctionValue*>>;
|
||||
|
||||
// Returns the value of the function named `name` in this class and its
|
||||
// parents, or nullopt if there is no such function.
|
||||
auto FindFunctionWithParents(std::string_view name,
|
||||
const ClassDeclaration& class_decl)
|
||||
-> std::optional<Nonnull<const FunctionValue*>>;
|
||||
|
||||
// Return the declaration of the member with the given name.
|
||||
auto FindMember(std::string_view name,
|
||||
llvm::ArrayRef<Nonnull<Declaration*>> members)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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: Class C, call 0
|
||||
// CHECK:STDOUT: Class D, call 0
|
||||
// CHECK:STDOUT: Class C, call 1
|
||||
// CHECK:STDOUT: Class C, call 2
|
||||
// CHECK:STDOUT: Class D, call 2
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class C {
|
||||
fn FunctionC(var i: i32) {
|
||||
Print("Class C, call {0}", i);
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
fn FunctionD(var i: i32) {
|
||||
C.FunctionC(i);
|
||||
Print("Class D, call {0}", i);
|
||||
}
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
// Calling function from class type
|
||||
D.FunctionD(0);
|
||||
// Note: D.FunctionC(0); is not accessible
|
||||
|
||||
// Calling function from class object
|
||||
var d: D = {};
|
||||
d.FunctionC(1);
|
||||
d.FunctionD(2);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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: Class 1
|
||||
// CHECK:STDOUT: Class 2
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class C {
|
||||
fn BasePrint(v: i32) {
|
||||
Print("Class {0}", v);
|
||||
}
|
||||
fn Method1[me:Self]() {
|
||||
me.BasePrint(me.value_c);
|
||||
}
|
||||
|
||||
var value_c: i32;
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
fn Method2[me:Self]() {
|
||||
me.BasePrint(me.value_d);
|
||||
}
|
||||
|
||||
var value_d: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var d: D = {.value_c = 1, .value_d = 2};
|
||||
d.Method1();
|
||||
d.Method2();
|
||||
return 0;
|
||||
}
|
||||
@@ -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: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class A {
|
||||
fn FunctionA() {}
|
||||
var var_a: i32;
|
||||
}
|
||||
|
||||
base class B extends A {
|
||||
fn FunctionB() {}
|
||||
var var_b: i32;
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
fn FunctionC() {}
|
||||
var var_c: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var c: C = {.var_a=1, .var_b=2, .var_c=3};
|
||||
c.FunctionA();
|
||||
c.FunctionB();
|
||||
c.FunctionC();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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: Class D
|
||||
// CHECK:STDOUT: d.value = 2
|
||||
// CHECK:STDOUT: d.GetValue() = 2
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class C {
|
||||
fn Method1() {
|
||||
Print("Class C");
|
||||
}
|
||||
fn GetValue[me: Self]() -> i32 {
|
||||
return me.value;
|
||||
}
|
||||
var value: i32;
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
fn Method1() {
|
||||
Print("Class D");
|
||||
}
|
||||
var value: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
// Initialize derived value first, base value second
|
||||
var d: D = {.value = 2, .value = 1};
|
||||
d.Method1();
|
||||
|
||||
Print("d.value = {0}", d.value);
|
||||
Print("d.GetValue() = {0}", d.GetValue());
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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: Read var_c=1
|
||||
// CHECK:STDOUT: Read var_d=2
|
||||
// CHECK:STDOUT: Assign var_c=3
|
||||
// CHECK:STDOUT: Assign var_d=4
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class C {
|
||||
var var_c: i32;
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
var var_d: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
// Initialization
|
||||
var d: D = {.var_c= 1, .var_d= 2};
|
||||
|
||||
// Read
|
||||
Print("Read var_c={0}", d.var_c);
|
||||
Print("Read var_d={0}", d.var_d);
|
||||
|
||||
// Assignment
|
||||
d.var_c = 3;
|
||||
d.var_d = 4;
|
||||
Print("Assign var_c={0}", d.var_c);
|
||||
Print("Assign var_d={0}", d.var_d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+1
-1
@@ -10,7 +10,7 @@ package ExplorerTest api;
|
||||
|
||||
abstract class C {
|
||||
fn F() {}
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_abstract_class.carbon:[[@LINE+1]]: Class prefixes `base` and `abstract` are not supported yet
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_abstract_class.carbon:[[@LINE+1]]: Class prefix `abstract` is not supported yet
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
|
||||
+4
-4
@@ -8,13 +8,13 @@
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class A {
|
||||
class C {
|
||||
fn F() {}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
fn F() {}
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_class_extends.carbon:[[@LINE+1]]: Class extension with `extends` is not supported yet
|
||||
class D extends C {
|
||||
fn G() {}
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_extends_final_class.carbon:[[@LINE+1]]: Base class `C` is `final` and cannot inherited. Add the `base` or `abstract` class prefix to `C` to allow it to be inherited
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
+4
-3
@@ -8,9 +8,10 @@
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
base class C {
|
||||
fn F() {}
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_base_class.carbon:[[@LINE+1]]: Class prefixes `base` and `abstract` are not supported yet
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_extends_non_class.carbon:[[@LINE+1]]: Expected a type, but got 3
|
||||
class C extends 3 {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
Reference in New Issue
Block a user