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:
Adrien Leravat
2022-11-29 13:14:30 -08:00
committed by GitHub
co-authored by Richard Smith
parent 84deb62aef
commit 46f4887cf7
18 changed files with 350 additions and 96 deletions
+13 -7
View File
@@ -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
+2
View File
@@ -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 = [
+53 -8
View File
@@ -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));
+60 -58
View File
@@ -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};
+9 -4
View File
@@ -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
+38 -7
View File
@@ -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;
}
+13 -3
View File
@@ -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),
+3 -1
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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;
}
+37
View File
@@ -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;
}