mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
If a name is not found in a class, perform lookup into base classes. (#3502)
This builds out a little infrastructure for one name scope to `extend` another. We'll need more refinement here to cover other cases, but this should provide some foundation for that future work. --------- Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
co-authored by
Jon Ross-Perkins
parent
23c7d7dd99
commit
de0c02ddae
@@ -87,7 +87,7 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
|
||||
if (self_import != unit_info.package_imports_map.end()) {
|
||||
auto& package_scope =
|
||||
context.name_scopes().Get(SemIR::NameScopeId::Package);
|
||||
package_scope.has_load_error = self_import->second.has_load_error;
|
||||
package_scope.has_error = self_import->second.has_load_error;
|
||||
|
||||
for (const auto& import : self_import->second.imports) {
|
||||
const auto& import_sem_ir = **import.unit_info->unit->sem_ir;
|
||||
@@ -96,7 +96,7 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
|
||||
|
||||
// If an import of the current package caused an error for the imported
|
||||
// file, it transitively affects the current file too.
|
||||
package_scope.has_load_error |= import_scope.has_load_error;
|
||||
package_scope.has_error |= import_scope.has_error;
|
||||
|
||||
auto ir_id = context.sem_ir().cross_ref_irs().Add(&import_sem_ir);
|
||||
|
||||
@@ -124,7 +124,7 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
|
||||
|
||||
// Push the scope.
|
||||
context.PushScope(package_inst, SemIR::NameScopeId::Package,
|
||||
package_scope.has_load_error);
|
||||
package_scope.has_error);
|
||||
} else {
|
||||
// Push the scope; there are no names to add.
|
||||
context.PushScope(package_inst, SemIR::NameScopeId::Package);
|
||||
|
||||
+60
-16
@@ -205,7 +205,8 @@ auto Context::ResolveIfLazyImportRef(SemIR::InstId inst_id) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto Context::LookupNameInDecl(Parse::NodeId parse_node, SemIR::NameId name_id,
|
||||
auto Context::LookupNameInDecl(Parse::NodeId /*parse_node*/,
|
||||
SemIR::NameId name_id,
|
||||
SemIR::NameScopeId scope_id) -> SemIR::InstId {
|
||||
if (scope_id == SemIR::NameScopeId::Invalid) {
|
||||
// Look for a name in the current scope only. There are two cases where the
|
||||
@@ -243,10 +244,16 @@ auto Context::LookupNameInDecl(Parse::NodeId parse_node, SemIR::NameId name_id,
|
||||
}
|
||||
return SemIR::InstId::Invalid;
|
||||
} else {
|
||||
// TODO: Once we support `extend`, do not look into `extend`ed scopes here,
|
||||
// following the same logic as above.
|
||||
return LookupQualifiedName(parse_node, name_id, scope_id,
|
||||
/*required=*/false);
|
||||
// We do not look into `extend`ed scopes here. A qualified name in a
|
||||
// declaration must specify the exact scope in which the name was originally
|
||||
// introduced:
|
||||
//
|
||||
// base class A { fn F(); }
|
||||
// class B { extend base: A; }
|
||||
//
|
||||
// // Error, no `F` in `B`.
|
||||
// fn B.F() {}
|
||||
return LookupNameInExactScope(name_id, name_scopes().Get(scope_id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,26 +303,63 @@ auto Context::LookupUnqualifiedName(Parse::NodeId parse_node,
|
||||
return SemIR::InstId::BuiltinError;
|
||||
}
|
||||
|
||||
auto Context::LookupQualifiedName(Parse::NodeId parse_node,
|
||||
SemIR::NameId name_id,
|
||||
SemIR::NameScopeId scope_id, bool required)
|
||||
auto Context::LookupNameInExactScope(SemIR::NameId name_id,
|
||||
const SemIR::NameScope& scope)
|
||||
-> SemIR::InstId {
|
||||
CARBON_CHECK(scope_id.is_valid()) << "No scope to perform lookup into";
|
||||
const auto& scope = name_scopes().Get(scope_id);
|
||||
if (auto it = scope.names.find(name_id); it != scope.names.end()) {
|
||||
ResolveIfLazyImportRef(it->second);
|
||||
return it->second;
|
||||
}
|
||||
return SemIR::InstId::Invalid;
|
||||
}
|
||||
|
||||
// TODO: Also perform lookups into `extend`ed scopes.
|
||||
auto Context::LookupQualifiedName(Parse::NodeId parse_node,
|
||||
SemIR::NameId name_id,
|
||||
SemIR::NameScopeId scope_id, bool required)
|
||||
-> SemIR::InstId {
|
||||
llvm::SmallVector<SemIR::NameScopeId> scope_ids = {scope_id};
|
||||
auto result_id = SemIR::InstId::Invalid;
|
||||
bool has_error = false;
|
||||
|
||||
if (!required) {
|
||||
return SemIR::InstId::Invalid;
|
||||
// Walk this scope and, if nothing is found here, the scopes it extends.
|
||||
while (!scope_ids.empty()) {
|
||||
const auto& scope = name_scopes().Get(scope_ids.pop_back_val());
|
||||
has_error |= scope.has_error;
|
||||
|
||||
auto scope_result_id = LookupNameInExactScope(name_id, scope);
|
||||
if (!scope_result_id.is_valid()) {
|
||||
// Nothing found in this scope: also look in its extended scopes.
|
||||
auto extended = llvm::reverse(scope.extended_scopes);
|
||||
scope_ids.append(extended.begin(), extended.end());
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is our second lookup result, diagnose an ambiguity.
|
||||
if (result_id.is_valid()) {
|
||||
// TODO: This is currently not reachable because the only scope that can
|
||||
// extend is a class scope, and it can only extend a single base class.
|
||||
// Add test coverage once this is possible.
|
||||
CARBON_DIAGNOSTIC(
|
||||
NameAmbiguousDueToExtend, Error,
|
||||
"Ambiguous use of name `{0}` found in multiple extended scopes.",
|
||||
std::string);
|
||||
emitter_->Emit(parse_node, NameAmbiguousDueToExtend,
|
||||
names().GetFormatted(name_id).str());
|
||||
// TODO: Add notes pointing to the scopes.
|
||||
return SemIR::InstId::BuiltinError;
|
||||
}
|
||||
|
||||
result_id = scope_result_id;
|
||||
}
|
||||
if (!scope.has_load_error) {
|
||||
DiagnoseNameNotFound(parse_node, name_id);
|
||||
|
||||
if (required && !result_id.is_valid()) {
|
||||
if (!has_error) {
|
||||
DiagnoseNameNotFound(parse_node, name_id);
|
||||
}
|
||||
return SemIR::InstId::BuiltinError;
|
||||
}
|
||||
return SemIR::InstId::BuiltinError;
|
||||
|
||||
return result_id;
|
||||
}
|
||||
|
||||
auto Context::PushScope(SemIR::InstId scope_inst_id,
|
||||
|
||||
@@ -85,6 +85,12 @@ class Context {
|
||||
auto LookupUnqualifiedName(Parse::NodeId parse_node, SemIR::NameId name_id)
|
||||
-> SemIR::InstId;
|
||||
|
||||
// Performs a name lookup in a specified scope, returning the referenced
|
||||
// instruction. Does not look into extended scopes. Returns an invalid
|
||||
// instruction if the name is not found.
|
||||
auto LookupNameInExactScope(SemIR::NameId name_id,
|
||||
const SemIR::NameScope& scope) -> SemIR::InstId;
|
||||
|
||||
// Performs a qualified name lookup in a specified scope and in scopes that
|
||||
// it extends, returning the referenced instruction.
|
||||
auto LookupQualifiedName(Parse::NodeId parse_node, SemIR::NameId name_id,
|
||||
|
||||
@@ -1032,12 +1032,19 @@ auto ConvertToValueOrRefExpr(Context& context, SemIR::InstId expr_id)
|
||||
}
|
||||
|
||||
auto ConvertToValueOfType(Context& context, Parse::NodeId parse_node,
|
||||
SemIR::InstId value_id, SemIR::TypeId type_id)
|
||||
SemIR::InstId expr_id, SemIR::TypeId type_id)
|
||||
-> SemIR::InstId {
|
||||
return Convert(context, parse_node, value_id,
|
||||
return Convert(context, parse_node, expr_id,
|
||||
{.kind = ConversionTarget::Value, .type_id = type_id});
|
||||
}
|
||||
|
||||
auto ConvertToValueOrRefOfType(Context& context, Parse::NodeId parse_node,
|
||||
SemIR::InstId expr_id, SemIR::TypeId type_id)
|
||||
-> SemIR::InstId {
|
||||
return Convert(context, parse_node, expr_id,
|
||||
{.kind = ConversionTarget::ValueOrRef, .type_id = type_id});
|
||||
}
|
||||
|
||||
auto ConvertToBoolValue(Context& context, Parse::NodeId parse_node,
|
||||
SemIR::InstId value_id) -> SemIR::InstId {
|
||||
return ConvertToValueOfType(
|
||||
|
||||
@@ -70,9 +70,15 @@ auto ConvertToValueExpr(Context& context, SemIR::InstId expr_id)
|
||||
auto ConvertToValueOrRefExpr(Context& context, SemIR::InstId expr_id)
|
||||
-> SemIR::InstId;
|
||||
|
||||
// Converts `value_id` to a value expression of type `type_id`.
|
||||
// Converts `expr_id` to a value expression of type `type_id`.
|
||||
auto ConvertToValueOfType(Context& context, Parse::NodeId parse_node,
|
||||
SemIR::InstId value_id, SemIR::TypeId type_id)
|
||||
SemIR::InstId expr_id, SemIR::TypeId type_id)
|
||||
-> SemIR::InstId;
|
||||
|
||||
// Convert the given expression to a value or reference expression of the given
|
||||
// type.
|
||||
auto ConvertToValueOrRefOfType(Context& context, Parse::NodeId parse_node,
|
||||
SemIR::InstId expr_id, SemIR::TypeId type_id)
|
||||
-> SemIR::InstId;
|
||||
|
||||
// Converts `value_id` to a value expression of type `bool`.
|
||||
|
||||
@@ -146,7 +146,7 @@ auto DeclNameStack::UpdateScopeIfNeeded(NameContext& name_context) -> void {
|
||||
name_context.state = NameContext::State::Resolved;
|
||||
name_context.target_scope_id = scope_id;
|
||||
context_->PushScope(name_context.resolved_inst_id, scope_id,
|
||||
context_->name_scopes().Get(scope_id).has_load_error);
|
||||
context_->name_scopes().Get(scope_id).has_error);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -174,6 +174,77 @@ auto HandleBaseColon(Context& /*context*/, Parse::NodeId /*parse_node*/)
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Information gathered about a base type specified in a `base` declaration.
|
||||
struct BaseInfo {
|
||||
// A `BaseInfo` representing an erroneous base.
|
||||
static const BaseInfo Error;
|
||||
|
||||
SemIR::TypeId type_id;
|
||||
SemIR::NameScopeId scope_id;
|
||||
};
|
||||
constexpr BaseInfo BaseInfo::Error = {.type_id = SemIR::TypeId::Error,
|
||||
.scope_id = SemIR::NameScopeId::Invalid};
|
||||
} // namespace
|
||||
|
||||
// If `type_id` is a class type, get its corresponding `SemIR::Class` object.
|
||||
// Otherwise returns `nullptr`.
|
||||
static auto TryGetAsClass(Context& context, SemIR::TypeId type_id)
|
||||
-> SemIR::Class* {
|
||||
auto class_type = context.types().TryGetAs<SemIR::ClassType>(type_id);
|
||||
if (!class_type) {
|
||||
return nullptr;
|
||||
}
|
||||
return &context.classes().Get(class_type->class_id);
|
||||
}
|
||||
|
||||
// Diagnoses an attempt to derive from a final type.
|
||||
static auto DiagnoseBaseIsFinal(Context& context, Parse::NodeId parse_node,
|
||||
SemIR::TypeId base_type_id) -> void {
|
||||
CARBON_DIAGNOSTIC(BaseIsFinal, Error,
|
||||
"Deriving from final type `{0}`. Base type must be an "
|
||||
"`abstract` or `base` class.",
|
||||
std::string);
|
||||
context.emitter().Emit(parse_node, BaseIsFinal,
|
||||
context.sem_ir().StringifyType(base_type_id));
|
||||
}
|
||||
|
||||
// Checks that the specified base type is valid.
|
||||
static auto CheckBaseType(Context& context, Parse::NodeId parse_node,
|
||||
SemIR::InstId base_expr_id) -> BaseInfo {
|
||||
auto base_type_id = ExprAsType(context, parse_node, base_expr_id);
|
||||
base_type_id = context.AsCompleteType(base_type_id, [&] {
|
||||
CARBON_DIAGNOSTIC(IncompleteTypeInBaseDecl, Error,
|
||||
"Base `{0}` is an incomplete type.", std::string);
|
||||
return context.emitter().Build(
|
||||
parse_node, IncompleteTypeInBaseDecl,
|
||||
context.sem_ir().StringifyType(base_type_id));
|
||||
});
|
||||
|
||||
if (base_type_id == SemIR::TypeId::Error) {
|
||||
return BaseInfo::Error;
|
||||
}
|
||||
|
||||
auto* base_class_info = TryGetAsClass(context, base_type_id);
|
||||
|
||||
// The base must not be a final class.
|
||||
if (!base_class_info) {
|
||||
// For now, we treat all types that aren't introduced by a `class`
|
||||
// declaration as being final classes.
|
||||
// TODO: Once we have a better idea of which types are considered to be
|
||||
// classes, produce a better diagnostic for deriving from a non-class type.
|
||||
DiagnoseBaseIsFinal(context, parse_node, base_type_id);
|
||||
return BaseInfo::Error;
|
||||
}
|
||||
if (base_class_info->inheritance_kind == SemIR::Class::Final) {
|
||||
DiagnoseBaseIsFinal(context, parse_node, base_type_id);
|
||||
}
|
||||
|
||||
CARBON_CHECK(base_class_info->scope_id.is_valid())
|
||||
<< "Complete class should have a scope";
|
||||
return {.type_id = base_type_id, .scope_id = base_class_info->scope_id};
|
||||
}
|
||||
|
||||
auto HandleBaseDecl(Context& context, Parse::NodeId parse_node) -> bool {
|
||||
auto base_type_expr_id = context.node_stack().PopExpr();
|
||||
|
||||
@@ -211,54 +282,39 @@ auto HandleBaseDecl(Context& context, Parse::NodeId parse_node) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto base_type_id = ExprAsType(context, parse_node, base_type_expr_id);
|
||||
base_type_id = context.AsCompleteType(base_type_id, [&] {
|
||||
CARBON_DIAGNOSTIC(IncompleteTypeInBaseDecl, Error,
|
||||
"Base `{0}` is an incomplete type.", std::string);
|
||||
return context.emitter().Build(
|
||||
parse_node, IncompleteTypeInBaseDecl,
|
||||
context.sem_ir().StringifyType(base_type_id));
|
||||
});
|
||||
|
||||
if (base_type_id != SemIR::TypeId::Error) {
|
||||
// For now, we treat all types that aren't introduced by a `class`
|
||||
// declaration as being final classes.
|
||||
// TODO: Once we have a better idea of which types are considered to be
|
||||
// classes, produce a better diagnostic for deriving from a non-class type.
|
||||
auto base_class = context.types().TryGetAs<SemIR::ClassType>(base_type_id);
|
||||
if (!base_class ||
|
||||
context.classes().Get(base_class->class_id).inheritance_kind ==
|
||||
SemIR::Class::Final) {
|
||||
CARBON_DIAGNOSTIC(BaseIsFinal, Error,
|
||||
"Deriving from final type `{0}`. Base type must be an "
|
||||
"`abstract` or `base` class.",
|
||||
std::string);
|
||||
context.emitter().Emit(parse_node, BaseIsFinal,
|
||||
context.sem_ir().StringifyType(base_type_id));
|
||||
}
|
||||
}
|
||||
auto base_info = CheckBaseType(context, parse_node, base_type_expr_id);
|
||||
|
||||
// The `base` value in the class scope has an unbound element type. Instance
|
||||
// binding will be performed when it's found by name lookup into an instance.
|
||||
auto field_type_inst_id = context.AddInst(SemIR::UnboundElementType{
|
||||
parse_node, context.GetBuiltinType(SemIR::BuiltinKind::TypeType),
|
||||
class_info.self_type_id, base_type_id});
|
||||
class_info.self_type_id, base_info.type_id});
|
||||
auto field_type_id = context.CanonicalizeType(field_type_inst_id);
|
||||
class_info.base_id = context.AddInst(SemIR::BaseDecl{
|
||||
parse_node, field_type_id, base_type_id,
|
||||
parse_node, field_type_id, base_info.type_id,
|
||||
SemIR::ElementIndex(
|
||||
context.args_type_info_stack().PeekCurrentBlockContents().size())});
|
||||
|
||||
// Add a corresponding field to the object representation of the class.
|
||||
// TODO: Consider whether we want to use `partial T` here.
|
||||
context.args_type_info_stack().AddInst(
|
||||
SemIR::StructTypeField{parse_node, SemIR::NameId::Base, base_type_id});
|
||||
context.args_type_info_stack().AddInst(SemIR::StructTypeField{
|
||||
parse_node, SemIR::NameId::Base, base_info.type_id});
|
||||
|
||||
// Bind the name `base` in the class to the base field.
|
||||
context.decl_name_stack().AddNameToLookup(
|
||||
context.decl_name_stack().MakeUnqualifiedName(parse_node,
|
||||
SemIR::NameId::Base),
|
||||
class_info.base_id);
|
||||
|
||||
// Extend the class scope with the base class.
|
||||
if (!!(modifiers & KeywordModifierSet::Extend)) {
|
||||
auto& class_scope = context.name_scopes().Get(class_info.scope_id);
|
||||
if (base_info.scope_id.is_valid()) {
|
||||
class_scope.extended_scopes.push_back(base_info.scope_id);
|
||||
} else {
|
||||
class_scope.has_error = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,8 +131,9 @@ auto HandleMemberAccessExpr(Context& context, Parse::NodeId parse_node)
|
||||
if (auto unbound_element_type =
|
||||
context.types().TryGetAs<SemIR::UnboundElementType>(
|
||||
member_type_id)) {
|
||||
// TODO: Check that the unbound element type describes a member of this
|
||||
// class. Perform a conversion of the base if necessary.
|
||||
// Convert the base to the type of the element if necessary.
|
||||
base_id = ConvertToValueOrRefOfType(
|
||||
context, parse_node, base_id, unbound_element_type->class_type_id);
|
||||
|
||||
// Find the specified element, which could be either a field or a base
|
||||
// class, and build an element access expression.
|
||||
|
||||
+1
@@ -67,6 +67,7 @@ fn Access(d: Derived) -> (i32, i32) {
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc12_20.2
|
||||
// CHECK:STDOUT: .d = %d
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Make() -> %return: Derived {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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
|
||||
|
||||
base class Base {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
var c: i32;
|
||||
}
|
||||
|
||||
class Derived {
|
||||
extend base: Base;
|
||||
|
||||
var d: i32;
|
||||
var e: i32;
|
||||
}
|
||||
|
||||
fn Access(p: Derived*) -> i32* {
|
||||
return &(*p).c;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- base_field.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.loc11_1.1: type = struct_type {.a: i32, .b: i32, .c: i32}
|
||||
// CHECK:STDOUT: %.loc11_1.2: type = ptr_type {.a: i32, .b: i32, .c: i32}
|
||||
// CHECK:STDOUT: %.loc18_1.1: type = struct_type {.base: Base, .d: i32, .e: i32}
|
||||
// CHECK:STDOUT: %.loc18_1.2: type = struct_type {.base: {.a: i32, .b: i32, .c: i32}*, .d: i32, .e: i32}
|
||||
// CHECK:STDOUT: %.loc18_1.3: type = ptr_type {.base: {.a: i32, .b: i32, .c: i32}*, .d: i32, .e: i32}
|
||||
// CHECK:STDOUT: %.loc13: type = ptr_type {.base: Base, .d: i32, .e: i32}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.Base = %Base.decl, .Derived = %Derived.decl, .Access = %Access}
|
||||
// CHECK:STDOUT: %Base.decl = class_decl @Base, ()
|
||||
// CHECK:STDOUT: %Base: type = class_type @Base
|
||||
// CHECK:STDOUT: %Derived.decl = class_decl @Derived, ()
|
||||
// CHECK:STDOUT: %Derived: type = class_type @Derived
|
||||
// CHECK:STDOUT: %Access: <function> = fn_decl @Access
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Base {
|
||||
// CHECK:STDOUT: %.loc8_8.1: type = unbound_element_type Base, i32
|
||||
// CHECK:STDOUT: %.loc8_8.2: <unbound element of class Base> = field_decl a, element0
|
||||
// CHECK:STDOUT: %a: <unbound element of class Base> = bind_name a, %.loc8_8.2
|
||||
// CHECK:STDOUT: %.loc9_8.1: type = unbound_element_type Base, i32
|
||||
// CHECK:STDOUT: %.loc9_8.2: <unbound element of class Base> = field_decl b, element1
|
||||
// CHECK:STDOUT: %b: <unbound element of class Base> = bind_name b, %.loc9_8.2
|
||||
// CHECK:STDOUT: %.loc10_8.1: type = unbound_element_type Base, i32
|
||||
// CHECK:STDOUT: %.loc10_8.2: <unbound element of class Base> = field_decl c, element2
|
||||
// CHECK:STDOUT: %c: <unbound element of class Base> = bind_name c, %.loc10_8.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .a = %a
|
||||
// CHECK:STDOUT: .b = %b
|
||||
// CHECK:STDOUT: .c = %c
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Derived {
|
||||
// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base
|
||||
// CHECK:STDOUT: %.loc14_20.1: type = unbound_element_type Derived, Base
|
||||
// CHECK:STDOUT: %.loc14_20.2: <unbound element of class Derived> = base_decl Base, element0
|
||||
// CHECK:STDOUT: %.loc16_8.1: type = unbound_element_type Derived, i32
|
||||
// CHECK:STDOUT: %.loc16_8.2: <unbound element of class Derived> = field_decl d, element1
|
||||
// CHECK:STDOUT: %d: <unbound element of class Derived> = bind_name d, %.loc16_8.2
|
||||
// CHECK:STDOUT: %.loc17_8.1: type = unbound_element_type Derived, i32
|
||||
// CHECK:STDOUT: %.loc17_8.2: <unbound element of class Derived> = field_decl e, element2
|
||||
// CHECK:STDOUT: %e: <unbound element of class Derived> = bind_name e, %.loc17_8.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc14_20.2
|
||||
// CHECK:STDOUT: .d = %d
|
||||
// CHECK:STDOUT: .e = %e
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Access(%p: Derived*) -> i32* {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: Derived* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc21_12: ref Derived = deref %p.ref
|
||||
// CHECK:STDOUT: %.loc21_15.1: ref Base = class_element_access %.loc21_12, element0
|
||||
// CHECK:STDOUT: %.loc21_15.2: ref Base = converted %.loc21_12, %.loc21_15.1
|
||||
// CHECK:STDOUT: %.loc21_15.3: ref i32 = class_element_access %.loc21_15.2, element2
|
||||
// CHECK:STDOUT: %.loc21_10: i32* = address_of %.loc21_15.3
|
||||
// CHECK:STDOUT: return %.loc21_10
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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
|
||||
|
||||
base class Base {
|
||||
fn F();
|
||||
}
|
||||
|
||||
class Derived {
|
||||
extend base: Base;
|
||||
|
||||
fn G() { F(); }
|
||||
fn H();
|
||||
}
|
||||
|
||||
fn Derived.H() {
|
||||
F();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- base_function_unqualified.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.loc9_1.1: type = struct_type {}
|
||||
// CHECK:STDOUT: %.loc9_1.2: type = tuple_type ()
|
||||
// CHECK:STDOUT: %.loc7: type = ptr_type {}
|
||||
// CHECK:STDOUT: %.loc16: type = struct_type {.base: Base}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.Base = %Base.decl, .Derived = %Derived.decl}
|
||||
// CHECK:STDOUT: %Base.decl = class_decl @Base, ()
|
||||
// CHECK:STDOUT: %Base: type = class_type @Base
|
||||
// CHECK:STDOUT: %Derived.decl = class_decl @Derived, ()
|
||||
// CHECK:STDOUT: %Derived: type = class_type @Derived
|
||||
// CHECK:STDOUT: %H: <function> = fn_decl @H
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Base {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Derived {
|
||||
// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base
|
||||
// CHECK:STDOUT: %.loc12_20.1: type = unbound_element_type Derived, Base
|
||||
// CHECK:STDOUT: %.loc12_20.2: <unbound element of class Derived> = base_decl Base, element0
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G
|
||||
// CHECK:STDOUT: %H: <function> = fn_decl @H
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc12_20.2
|
||||
// CHECK:STDOUT: .G = %G
|
||||
// CHECK:STDOUT: .H = %H
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: <function> = name_ref F, @Base.%F
|
||||
// CHECK:STDOUT: %.loc14: init () = call %F.ref()
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @H() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: <function> = name_ref F, @Base.%F
|
||||
// CHECK:STDOUT: %.loc19: init () = call %F.ref()
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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
|
||||
|
||||
base class Base {
|
||||
var a: i32;
|
||||
|
||||
fn F[addr self: Self*]();
|
||||
}
|
||||
|
||||
fn Base.F[addr self: Base*]() {
|
||||
(*self).a = 1;
|
||||
}
|
||||
|
||||
class Derived {
|
||||
extend base: Base;
|
||||
}
|
||||
|
||||
fn Call(p: Derived*) {
|
||||
(*p).F();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- base_method.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.loc11: type = struct_type {.a: i32}
|
||||
// CHECK:STDOUT: %.loc7: type = ptr_type {.a: i32}
|
||||
// CHECK:STDOUT: %.loc19_1.1: type = struct_type {.base: Base}
|
||||
// CHECK:STDOUT: %.loc19_1.2: type = struct_type {.base: {.a: i32}*}
|
||||
// CHECK:STDOUT: %.loc17: type = ptr_type {.base: Base}
|
||||
// CHECK:STDOUT: %.loc22: type = tuple_type ()
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.Base = %Base.decl, .Derived = %Derived.decl, .Call = %Call}
|
||||
// CHECK:STDOUT: %Base.decl = class_decl @Base, ()
|
||||
// CHECK:STDOUT: %Base: type = class_type @Base
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: %Derived.decl = class_decl @Derived, ()
|
||||
// CHECK:STDOUT: %Derived: type = class_type @Derived
|
||||
// CHECK:STDOUT: %Call: <function> = fn_decl @Call
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Base {
|
||||
// CHECK:STDOUT: %.loc8_8.1: type = unbound_element_type Base, i32
|
||||
// CHECK:STDOUT: %.loc8_8.2: <unbound element of class Base> = field_decl a, element0
|
||||
// CHECK:STDOUT: %a: <unbound element of class Base> = bind_name a, %.loc8_8.2
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .a = %a
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Derived {
|
||||
// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base
|
||||
// CHECK:STDOUT: %.loc18_20.1: type = unbound_element_type Derived, Base
|
||||
// CHECK:STDOUT: %.loc18_20.2: <unbound element of class Derived> = base_decl Base, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc18_20.2
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F[%self.addr: Base*]() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %self.ref: Base* = name_ref self, %self.addr
|
||||
// CHECK:STDOUT: %.loc14_4: ref Base = deref %self.ref
|
||||
// CHECK:STDOUT: %.loc14_10: ref i32 = class_element_access %.loc14_4, element0
|
||||
// CHECK:STDOUT: %.loc14_15: i32 = int_literal 1
|
||||
// CHECK:STDOUT: assign %.loc14_10, %.loc14_15
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Call(%p: Derived*) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: Derived* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc22_4.1: ref Derived = deref %p.ref
|
||||
// CHECK:STDOUT: %.loc22_7: <bound method> = bound_method %.loc22_4.1, @Base.%F
|
||||
// CHECK:STDOUT: %.loc22_4.2: Derived* = address_of %.loc22_4.1
|
||||
// CHECK:STDOUT: %.loc22_9.1: ref Derived = deref %.loc22_4.2
|
||||
// CHECK:STDOUT: %.loc22_9.2: ref Base = class_element_access %.loc22_9.1, element0
|
||||
// CHECK:STDOUT: %.loc22_9.3: Base* = address_of %.loc22_9.2
|
||||
// CHECK:STDOUT: %.loc22_9.4: Base* = converted %.loc22_4.2, %.loc22_9.3
|
||||
// CHECK:STDOUT: %.loc22_9.5: init () = call %.loc22_7(%.loc22_9.4)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,134 @@
|
||||
// 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
|
||||
|
||||
base class A {
|
||||
fn F[addr self: Self*]();
|
||||
}
|
||||
|
||||
base class B {
|
||||
extend base: A;
|
||||
fn F[addr self: Self*]();
|
||||
}
|
||||
|
||||
class C {
|
||||
extend base: B;
|
||||
fn F[addr self: Self*]();
|
||||
}
|
||||
|
||||
class D {
|
||||
extend base: B;
|
||||
}
|
||||
|
||||
fn Call(a: A*, b: B*, c: C*, d: D*) {
|
||||
(*a).F();
|
||||
(*b).F();
|
||||
(*c).F();
|
||||
(*d).F();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- base_method_shadow.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.loc9_1.1: type = struct_type {}
|
||||
// CHECK:STDOUT: %.loc9_1.2: type = tuple_type ()
|
||||
// CHECK:STDOUT: %.loc7: type = ptr_type {}
|
||||
// CHECK:STDOUT: %.loc14_1.1: type = struct_type {.base: A}
|
||||
// CHECK:STDOUT: %.loc14_1.2: type = struct_type {.base: {}*}
|
||||
// CHECK:STDOUT: %.loc11: type = ptr_type {.base: A}
|
||||
// CHECK:STDOUT: %.loc19_1.1: type = struct_type {.base: B}
|
||||
// CHECK:STDOUT: %.loc19_1.2: type = struct_type {.base: {.base: A}*}
|
||||
// CHECK:STDOUT: %.loc16: type = ptr_type {.base: B}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.A = %A.decl, .B = %B.decl, .C = %C.decl, .D = %D.decl, .Call = %Call}
|
||||
// CHECK:STDOUT: %A.decl = class_decl @A, ()
|
||||
// CHECK:STDOUT: %A: type = class_type @A
|
||||
// CHECK:STDOUT: %B.decl = class_decl @B, ()
|
||||
// CHECK:STDOUT: %B: type = class_type @B
|
||||
// CHECK:STDOUT: %C.decl = class_decl @C, ()
|
||||
// CHECK:STDOUT: %C: type = class_type @C
|
||||
// CHECK:STDOUT: %D.decl = class_decl @D, ()
|
||||
// CHECK:STDOUT: %D: type = class_type @D
|
||||
// CHECK:STDOUT: %Call: <function> = fn_decl @Call
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @A {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @B {
|
||||
// CHECK:STDOUT: %A.ref: type = name_ref A, file.%A
|
||||
// CHECK:STDOUT: %.loc12_17.1: type = unbound_element_type B, A
|
||||
// CHECK:STDOUT: %.loc12_17.2: <unbound element of class B> = base_decl A, element0
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc12_17.2
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @C {
|
||||
// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B
|
||||
// CHECK:STDOUT: %.loc17_17.1: type = unbound_element_type C, B
|
||||
// CHECK:STDOUT: %.loc17_17.2: <unbound element of class C> = base_decl B, element0
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.3
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc17_17.2
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: extend name_scope2
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @D {
|
||||
// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B
|
||||
// CHECK:STDOUT: %.loc22_17.1: type = unbound_element_type D, B
|
||||
// CHECK:STDOUT: %.loc22_17.2: <unbound element of class D> = base_decl B, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc22_17.2
|
||||
// CHECK:STDOUT: extend name_scope2
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.1[%self.addr: A*]();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.2[%self.addr: B*]();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.3[%self.addr: C*]();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Call(%a: A*, %b: B*, %c: C*, %d: D*) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %a.ref: A* = name_ref a, %a
|
||||
// CHECK:STDOUT: %.loc26_4.1: ref A = deref %a.ref
|
||||
// CHECK:STDOUT: %.loc26_7: <bound method> = bound_method %.loc26_4.1, @A.%F
|
||||
// CHECK:STDOUT: %.loc26_4.2: A* = address_of %.loc26_4.1
|
||||
// CHECK:STDOUT: %.loc26_9: init () = call %.loc26_7(%.loc26_4.2)
|
||||
// CHECK:STDOUT: %b.ref: B* = name_ref b, %b
|
||||
// CHECK:STDOUT: %.loc27_4.1: ref B = deref %b.ref
|
||||
// CHECK:STDOUT: %.loc27_7: <bound method> = bound_method %.loc27_4.1, @B.%F
|
||||
// CHECK:STDOUT: %.loc27_4.2: B* = address_of %.loc27_4.1
|
||||
// CHECK:STDOUT: %.loc27_9: init () = call %.loc27_7(%.loc27_4.2)
|
||||
// CHECK:STDOUT: %c.ref: C* = name_ref c, %c
|
||||
// CHECK:STDOUT: %.loc28_4.1: ref C = deref %c.ref
|
||||
// CHECK:STDOUT: %.loc28_7: <bound method> = bound_method %.loc28_4.1, @C.%F
|
||||
// CHECK:STDOUT: %.loc28_4.2: C* = address_of %.loc28_4.1
|
||||
// CHECK:STDOUT: %.loc28_9: init () = call %.loc28_7(%.loc28_4.2)
|
||||
// CHECK:STDOUT: %d.ref: D* = name_ref d, %d
|
||||
// CHECK:STDOUT: %.loc29_4.1: ref D = deref %d.ref
|
||||
// CHECK:STDOUT: %.loc29_7: <bound method> = bound_method %.loc29_4.1, @B.%F
|
||||
// CHECK:STDOUT: %.loc29_4.2: D* = address_of %.loc29_4.1
|
||||
// CHECK:STDOUT: %.loc29_9.1: ref D = deref %.loc29_4.2
|
||||
// CHECK:STDOUT: %.loc29_9.2: ref B = class_element_access %.loc29_9.1, element0
|
||||
// CHECK:STDOUT: %.loc29_9.3: B* = address_of %.loc29_9.2
|
||||
// CHECK:STDOUT: %.loc29_9.4: B* = converted %.loc29_4.2, %.loc29_9.3
|
||||
// CHECK:STDOUT: %.loc29_9.5: init () = call %.loc29_7(%.loc29_9.4)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -87,6 +87,7 @@ fn ConvertInit() {
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc12_17.2
|
||||
// CHECK:STDOUT: .b = %b
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @C {
|
||||
@@ -100,6 +101,7 @@ fn ConvertInit() {
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc17_17.2
|
||||
// CHECK:STDOUT: .c = %c
|
||||
// CHECK:STDOUT: extend name_scope2
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @ConvertCToB(%p: C*) -> B* {
|
||||
|
||||
@@ -71,6 +71,7 @@ fn Access(d: Derived) -> (i32, i32) {
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc12_24.2
|
||||
// CHECK:STDOUT: .d = %d
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Make() -> %return: Derived {
|
||||
|
||||
+187
-62
@@ -5,7 +5,19 @@
|
||||
// AUTOUPDATE
|
||||
|
||||
base class Base {}
|
||||
class Final {}
|
||||
class Final {
|
||||
var a: i32;
|
||||
}
|
||||
|
||||
class DeriveFromError {
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:16: ERROR: Name `error` not found.
|
||||
// CHECK:STDERR: extend base: error;
|
||||
// CHECK:STDERR: ^~~~~
|
||||
extend base: error;
|
||||
}
|
||||
|
||||
// This should not produce an error.
|
||||
fn AccessMemberWithInvalidBaseError(p: DeriveFromError*) -> i32 { return (*p).n; }
|
||||
|
||||
class DeriveFromNonType {
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:3: ERROR: Cannot implicitly convert from `i32` to `type`.
|
||||
@@ -14,6 +26,8 @@ class DeriveFromNonType {
|
||||
extend base: 32;
|
||||
}
|
||||
|
||||
fn AccessMemberWithInvalidBasNonType(p: DeriveFromNonType*) -> i32 { return (*p).n; }
|
||||
|
||||
class DeriveFromi32 {
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:3: ERROR: Deriving from final type `i32`. Base type must be an `abstract` or `base` class.
|
||||
// CHECK:STDERR: extend base: i32;
|
||||
@@ -23,8 +37,13 @@ class DeriveFromi32 {
|
||||
|
||||
// It's not really important whether this conversion produces an error or not,
|
||||
// but it shouldn't crash.
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:53: ERROR: Cannot implicitly convert from `DeriveFromi32*` to `i32*`.
|
||||
// CHECK:STDERR: fn ConvertToBadBasei32(p: DeriveFromi32*) -> i32* { return p; }
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
fn ConvertToBadBasei32(p: DeriveFromi32*) -> i32* { return p; }
|
||||
|
||||
fn AccessMemberWithInvalidBasei32(p: DeriveFromi32*) -> i32 { return (*p).n; }
|
||||
|
||||
class DeriveFromTuple {
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:3: ERROR: Deriving from final type `(Base,)`. Base type must be an `abstract` or `base` class.
|
||||
// CHECK:STDERR: extend base: (Base,);
|
||||
@@ -32,8 +51,13 @@ class DeriveFromTuple {
|
||||
extend base: (Base,);
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:61: ERROR: Cannot implicitly convert from `DeriveFromTuple*` to `(Base,)*`.
|
||||
// CHECK:STDERR: fn ConvertToBadBaseTuple(p: DeriveFromTuple*) -> (Base,)* { return p; }
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
fn ConvertToBadBaseTuple(p: DeriveFromTuple*) -> (Base,)* { return p; }
|
||||
|
||||
fn AccessMemberWithInvalidBaseTuple(p: DeriveFromTuple*) -> i32 { return (*p).n; }
|
||||
|
||||
// TODO: Should we allow this?
|
||||
// We do allow `{.base = {.a: i32, .b: i32}}`.
|
||||
class DeriveFromStruct {
|
||||
@@ -43,8 +67,14 @@ class DeriveFromStruct {
|
||||
extend base: {.a: i32, .b: i32};
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:74: ERROR: Cannot implicitly convert from `DeriveFromStruct*` to `{.a: i32, .b: i32}*`.
|
||||
// CHECK:STDERR: fn ConvertToBadBaseStruct(p: DeriveFromStruct*) -> {.a: i32, .b: i32}* { return p; }
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
fn ConvertToBadBaseStruct(p: DeriveFromStruct*) -> {.a: i32, .b: i32}* { return p; }
|
||||
|
||||
// It would be OK to reject this if we start actually looking in the struct type.
|
||||
fn AccessMemberWithInvalidBaseStruct(p: DeriveFromStruct*) -> i32 { return (*p).n; }
|
||||
|
||||
base class Incomplete;
|
||||
|
||||
class DeriveFromIncomplete {
|
||||
@@ -62,6 +92,8 @@ class DeriveFromIncomplete {
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
fn ConvertToBadBaseIncomplete(p: DeriveFromIncomplete*) -> Incomplete* { return p; }
|
||||
|
||||
fn AccessMemberWithInvalidBaseIncomplete(p: DeriveFromIncomplete*) -> i32 { return (*p).n; }
|
||||
|
||||
class DeriveFromFinal {
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:3: ERROR: Deriving from final type `Final`. Base type must be an `abstract` or `base` class.
|
||||
// CHECK:STDERR: extend base: Final;
|
||||
@@ -69,58 +101,76 @@ class DeriveFromFinal {
|
||||
extend base: Final;
|
||||
}
|
||||
|
||||
fn ConvertToBadBaseFinal(p: DeriveFromFinal*) -> Final* { return p; }
|
||||
// For error recovery purposes, we derive from the final type anyway.
|
||||
fn ConvertToBadBaseFinal(p: DeriveFromFinal*) -> Final* {
|
||||
return p;
|
||||
}
|
||||
|
||||
fn AccessMemberWithInvalidBaseFinal_WithMember(p: DeriveFromFinal*) -> i32 {
|
||||
return (*p).a;
|
||||
}
|
||||
|
||||
fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
|
||||
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+3]]:10: ERROR: Name `b` not found.
|
||||
// CHECK:STDERR: return (*p).b;
|
||||
// CHECK:STDERR: ^~~~~~
|
||||
return (*p).b;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- fail_base_bad_type.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.loc7_18.1: type = struct_type {}
|
||||
// CHECK:STDOUT: %.loc15: type = struct_type {.base: <error>}
|
||||
// CHECK:STDOUT: %.loc22: type = struct_type {.base: i32}
|
||||
// CHECK:STDOUT: %.loc17: type = ptr_type {.base: i32}
|
||||
// CHECK:STDOUT: %.loc32_22: type = tuple_type (type)
|
||||
// CHECK:STDOUT: %.loc32_23.1: type = tuple_type (Base)
|
||||
// CHECK:STDOUT: %.loc10: type = struct_type {.a: i32}
|
||||
// CHECK:STDOUT: %.loc17: type = struct_type {.base: <error>}
|
||||
// CHECK:STDOUT: %.loc12: type = ptr_type {.base: <error>}
|
||||
// CHECK:STDOUT: %.loc51_22: type = tuple_type (type)
|
||||
// CHECK:STDOUT: %.loc51_23.1: type = tuple_type (Base)
|
||||
// CHECK:STDOUT: %.loc7_18.2: type = tuple_type ()
|
||||
// CHECK:STDOUT: %.loc7_17: type = ptr_type {}
|
||||
// CHECK:STDOUT: %.loc32_23.2: type = tuple_type ({}*)
|
||||
// CHECK:STDOUT: %.loc33_1.1: type = struct_type {.base: (Base,)}
|
||||
// CHECK:STDOUT: %.loc33_1.2: type = struct_type {.base: ({}*,)}
|
||||
// CHECK:STDOUT: %.loc28: type = ptr_type {.base: (Base,)}
|
||||
// CHECK:STDOUT: %.loc43: type = ptr_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %.loc44_1.1: type = struct_type {.base: {.a: i32, .b: i32}}
|
||||
// CHECK:STDOUT: %.loc44_1.2: type = struct_type {.base: {.a: i32, .b: i32}*}
|
||||
// CHECK:STDOUT: %.loc39: type = ptr_type {.base: {.a: i32, .b: i32}}
|
||||
// CHECK:STDOUT: %.loc50: type = ptr_type {.base: <error>}
|
||||
// CHECK:STDOUT: %.loc70_1.1: type = struct_type {.base: Final}
|
||||
// CHECK:STDOUT: %.loc70_1.2: type = struct_type {.base: {}*}
|
||||
// CHECK:STDOUT: %.loc65: type = ptr_type {.base: Final}
|
||||
// CHECK:STDOUT: %.loc51_23.2: type = tuple_type ({}*)
|
||||
// CHECK:STDOUT: %.loc67: type = ptr_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %.loc8: type = ptr_type {.a: i32}
|
||||
// CHECK:STDOUT: %.loc102_1.1: type = struct_type {.base: Final}
|
||||
// CHECK:STDOUT: %.loc102_1.2: type = struct_type {.base: {.a: i32}*}
|
||||
// CHECK:STDOUT: %.loc97: type = ptr_type {.base: Final}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.Base = %Base.decl, .Final = %Final.decl, .DeriveFromNonType = %DeriveFromNonType.decl, .DeriveFromi32 = %DeriveFromi32.decl, .ConvertToBadBasei32 = %ConvertToBadBasei32, .DeriveFromTuple = %DeriveFromTuple.decl, .ConvertToBadBaseTuple = %ConvertToBadBaseTuple, .DeriveFromStruct = %DeriveFromStruct.decl, .ConvertToBadBaseStruct = %ConvertToBadBaseStruct, .Incomplete = %Incomplete.decl, .DeriveFromIncomplete = %DeriveFromIncomplete.decl, .ConvertToBadBaseIncomplete = %ConvertToBadBaseIncomplete, .DeriveFromFinal = %DeriveFromFinal.decl, .ConvertToBadBaseFinal = %ConvertToBadBaseFinal}
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.Base = %Base.decl, .Final = %Final.decl, .DeriveFromError = %DeriveFromError.decl, .AccessMemberWithInvalidBaseError = %AccessMemberWithInvalidBaseError, .DeriveFromNonType = %DeriveFromNonType.decl, .AccessMemberWithInvalidBasNonType = %AccessMemberWithInvalidBasNonType, .DeriveFromi32 = %DeriveFromi32.decl, .ConvertToBadBasei32 = %ConvertToBadBasei32, .AccessMemberWithInvalidBasei32 = %AccessMemberWithInvalidBasei32, .DeriveFromTuple = %DeriveFromTuple.decl, .ConvertToBadBaseTuple = %ConvertToBadBaseTuple, .AccessMemberWithInvalidBaseTuple = %AccessMemberWithInvalidBaseTuple, .DeriveFromStruct = %DeriveFromStruct.decl, .ConvertToBadBaseStruct = %ConvertToBadBaseStruct, .AccessMemberWithInvalidBaseStruct = %AccessMemberWithInvalidBaseStruct, .Incomplete = %Incomplete.decl, .DeriveFromIncomplete = %DeriveFromIncomplete.decl, .ConvertToBadBaseIncomplete = %ConvertToBadBaseIncomplete, .AccessMemberWithInvalidBaseIncomplete = %AccessMemberWithInvalidBaseIncomplete, .DeriveFromFinal = %DeriveFromFinal.decl, .ConvertToBadBaseFinal = %ConvertToBadBaseFinal, .AccessMemberWithInvalidBaseFinal_WithMember = %AccessMemberWithInvalidBaseFinal_WithMember, .AccessMemberWithInvalidBaseFinal_NoMember = %AccessMemberWithInvalidBaseFinal_NoMember}
|
||||
// CHECK:STDOUT: %Base.decl = class_decl @Base, ()
|
||||
// CHECK:STDOUT: %Base: type = class_type @Base
|
||||
// CHECK:STDOUT: %Final.decl = class_decl @Final, ()
|
||||
// CHECK:STDOUT: %Final: type = class_type @Final
|
||||
// CHECK:STDOUT: %DeriveFromError.decl = class_decl @DeriveFromError, ()
|
||||
// CHECK:STDOUT: %DeriveFromError: type = class_type @DeriveFromError
|
||||
// CHECK:STDOUT: %AccessMemberWithInvalidBaseError: <function> = fn_decl @AccessMemberWithInvalidBaseError
|
||||
// CHECK:STDOUT: %DeriveFromNonType.decl = class_decl @DeriveFromNonType, ()
|
||||
// CHECK:STDOUT: %DeriveFromNonType: type = class_type @DeriveFromNonType
|
||||
// CHECK:STDOUT: %AccessMemberWithInvalidBasNonType: <function> = fn_decl @AccessMemberWithInvalidBasNonType
|
||||
// CHECK:STDOUT: %DeriveFromi32.decl = class_decl @DeriveFromi32, ()
|
||||
// CHECK:STDOUT: %DeriveFromi32: type = class_type @DeriveFromi32
|
||||
// CHECK:STDOUT: %ConvertToBadBasei32: <function> = fn_decl @ConvertToBadBasei32
|
||||
// CHECK:STDOUT: %AccessMemberWithInvalidBasei32: <function> = fn_decl @AccessMemberWithInvalidBasei32
|
||||
// CHECK:STDOUT: %DeriveFromTuple.decl = class_decl @DeriveFromTuple, ()
|
||||
// CHECK:STDOUT: %DeriveFromTuple: type = class_type @DeriveFromTuple
|
||||
// CHECK:STDOUT: %ConvertToBadBaseTuple: <function> = fn_decl @ConvertToBadBaseTuple
|
||||
// CHECK:STDOUT: %AccessMemberWithInvalidBaseTuple: <function> = fn_decl @AccessMemberWithInvalidBaseTuple
|
||||
// CHECK:STDOUT: %DeriveFromStruct.decl = class_decl @DeriveFromStruct, ()
|
||||
// CHECK:STDOUT: %DeriveFromStruct: type = class_type @DeriveFromStruct
|
||||
// CHECK:STDOUT: %ConvertToBadBaseStruct: <function> = fn_decl @ConvertToBadBaseStruct
|
||||
// CHECK:STDOUT: %AccessMemberWithInvalidBaseStruct: <function> = fn_decl @AccessMemberWithInvalidBaseStruct
|
||||
// CHECK:STDOUT: %Incomplete.decl = class_decl @Incomplete, ()
|
||||
// CHECK:STDOUT: %Incomplete: type = class_type @Incomplete
|
||||
// CHECK:STDOUT: %DeriveFromIncomplete.decl = class_decl @DeriveFromIncomplete, ()
|
||||
// CHECK:STDOUT: %DeriveFromIncomplete: type = class_type @DeriveFromIncomplete
|
||||
// CHECK:STDOUT: %ConvertToBadBaseIncomplete: <function> = fn_decl @ConvertToBadBaseIncomplete
|
||||
// CHECK:STDOUT: %AccessMemberWithInvalidBaseIncomplete: <function> = fn_decl @AccessMemberWithInvalidBaseIncomplete
|
||||
// CHECK:STDOUT: %DeriveFromFinal.decl = class_decl @DeriveFromFinal, ()
|
||||
// CHECK:STDOUT: %DeriveFromFinal: type = class_type @DeriveFromFinal
|
||||
// CHECK:STDOUT: %ConvertToBadBaseFinal: <function> = fn_decl @ConvertToBadBaseFinal
|
||||
// CHECK:STDOUT: %AccessMemberWithInvalidBaseFinal_WithMember: <function> = fn_decl @AccessMemberWithInvalidBaseFinal_WithMember
|
||||
// CHECK:STDOUT: %AccessMemberWithInvalidBaseFinal_NoMember: <function> = fn_decl @AccessMemberWithInvalidBaseFinal_NoMember
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Base {
|
||||
@@ -129,95 +179,143 @@ fn ConvertToBadBaseFinal(p: DeriveFromFinal*) -> Final* { return p; }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Final {
|
||||
// CHECK:STDOUT: %.loc9_8.1: type = unbound_element_type Final, i32
|
||||
// CHECK:STDOUT: %.loc9_8.2: <unbound element of class Final> = field_decl a, element0
|
||||
// CHECK:STDOUT: %a: <unbound element of class Final> = bind_name a, %.loc9_8.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .a = %a
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @DeriveFromError {
|
||||
// CHECK:STDOUT: %error.ref: <error> = name_ref error, <error>
|
||||
// CHECK:STDOUT: %.loc16_21.1: type = unbound_element_type DeriveFromError, <error>
|
||||
// CHECK:STDOUT: %.loc16_21.2: <unbound element of class DeriveFromError> = base_decl <error>, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc16_21.2
|
||||
// CHECK:STDOUT: has_error
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @DeriveFromNonType {
|
||||
// CHECK:STDOUT: %.loc14_16: i32 = int_literal 32
|
||||
// CHECK:STDOUT: %.loc14_18.1: type = unbound_element_type DeriveFromNonType, <error>
|
||||
// CHECK:STDOUT: %.loc14_18.2: <unbound element of class DeriveFromNonType> = base_decl <error>, element0
|
||||
// CHECK:STDOUT: %.loc26_16: i32 = int_literal 32
|
||||
// CHECK:STDOUT: %.loc26_18.1: type = unbound_element_type DeriveFromNonType, <error>
|
||||
// CHECK:STDOUT: %.loc26_18.2: <unbound element of class DeriveFromNonType> = base_decl <error>, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc14_18.2
|
||||
// CHECK:STDOUT: .base = %.loc26_18.2
|
||||
// CHECK:STDOUT: has_error
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @DeriveFromi32 {
|
||||
// CHECK:STDOUT: %.loc21_19.1: type = unbound_element_type DeriveFromi32, i32
|
||||
// CHECK:STDOUT: %.loc21_19.2: <unbound element of class DeriveFromi32> = base_decl i32, element0
|
||||
// CHECK:STDOUT: %.loc35_19.1: type = unbound_element_type DeriveFromi32, <error>
|
||||
// CHECK:STDOUT: %.loc35_19.2: <unbound element of class DeriveFromi32> = base_decl <error>, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc21_19.2
|
||||
// CHECK:STDOUT: .base = %.loc35_19.2
|
||||
// CHECK:STDOUT: has_error
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @DeriveFromTuple {
|
||||
// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base
|
||||
// CHECK:STDOUT: %.loc32_22: (type,) = tuple_literal (%Base.ref)
|
||||
// CHECK:STDOUT: %.loc32_23.1: type = converted %.loc32_22, constants.%.loc32_23.1
|
||||
// CHECK:STDOUT: %.loc32_23.2: type = unbound_element_type DeriveFromTuple, (Base,)
|
||||
// CHECK:STDOUT: %.loc32_23.3: <unbound element of class DeriveFromTuple> = base_decl (Base,), element0
|
||||
// CHECK:STDOUT: %.loc51_22: (type,) = tuple_literal (%Base.ref)
|
||||
// CHECK:STDOUT: %.loc51_23.1: type = converted %.loc51_22, constants.%.loc51_23.1
|
||||
// CHECK:STDOUT: %.loc51_23.2: type = unbound_element_type DeriveFromTuple, <error>
|
||||
// CHECK:STDOUT: %.loc51_23.3: <unbound element of class DeriveFromTuple> = base_decl <error>, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc32_23.3
|
||||
// CHECK:STDOUT: .base = %.loc51_23.3
|
||||
// CHECK:STDOUT: has_error
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @DeriveFromStruct {
|
||||
// CHECK:STDOUT: %.loc43_33: type = struct_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %.loc43_34.1: type = unbound_element_type DeriveFromStruct, {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %.loc43_34.2: <unbound element of class DeriveFromStruct> = base_decl {.a: i32, .b: i32}, element0
|
||||
// CHECK:STDOUT: %.loc67_33: type = struct_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %.loc67_34.1: type = unbound_element_type DeriveFromStruct, <error>
|
||||
// CHECK:STDOUT: %.loc67_34.2: <unbound element of class DeriveFromStruct> = base_decl <error>, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc43_34.2
|
||||
// CHECK:STDOUT: .base = %.loc67_34.2
|
||||
// CHECK:STDOUT: has_error
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Incomplete;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @DeriveFromIncomplete {
|
||||
// CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, file.%Incomplete
|
||||
// CHECK:STDOUT: %.loc57_26.1: type = unbound_element_type DeriveFromIncomplete, <error>
|
||||
// CHECK:STDOUT: %.loc57_26.2: <unbound element of class DeriveFromIncomplete> = base_decl <error>, element0
|
||||
// CHECK:STDOUT: %.loc87_26.1: type = unbound_element_type DeriveFromIncomplete, <error>
|
||||
// CHECK:STDOUT: %.loc87_26.2: <unbound element of class DeriveFromIncomplete> = base_decl <error>, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc57_26.2
|
||||
// CHECK:STDOUT: .base = %.loc87_26.2
|
||||
// CHECK:STDOUT: has_error
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @DeriveFromFinal {
|
||||
// CHECK:STDOUT: %Final.ref: type = name_ref Final, file.%Final
|
||||
// CHECK:STDOUT: %.loc69_21.1: type = unbound_element_type DeriveFromFinal, Final
|
||||
// CHECK:STDOUT: %.loc69_21.2: <unbound element of class DeriveFromFinal> = base_decl Final, element0
|
||||
// CHECK:STDOUT: %.loc101_21.1: type = unbound_element_type DeriveFromFinal, Final
|
||||
// CHECK:STDOUT: %.loc101_21.2: <unbound element of class DeriveFromFinal> = base_decl Final, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc69_21.2
|
||||
// CHECK:STDOUT: .base = %.loc101_21.2
|
||||
// CHECK:STDOUT: extend name_scope2
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessMemberWithInvalidBaseError(%p: DeriveFromError*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromError* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc20: ref DeriveFromError = deref %p.ref
|
||||
// CHECK:STDOUT: %n.ref: <error> = name_ref n, <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessMemberWithInvalidBasNonType(%p: DeriveFromNonType*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromNonType* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc29: ref DeriveFromNonType = deref %p.ref
|
||||
// CHECK:STDOUT: %n.ref: <error> = name_ref n, <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @ConvertToBadBasei32(%p: DeriveFromi32*) -> i32* {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromi32* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc26_61.1: ref DeriveFromi32 = deref %p.ref
|
||||
// CHECK:STDOUT: %.loc26_61.2: ref i32 = class_element_access %.loc26_61.1, element0
|
||||
// CHECK:STDOUT: %.loc26_61.3: i32* = address_of %.loc26_61.2
|
||||
// CHECK:STDOUT: %.loc26_61.4: i32* = converted %p.ref, %.loc26_61.3
|
||||
// CHECK:STDOUT: return %.loc26_61.4
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessMemberWithInvalidBasei32(%p: DeriveFromi32*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromi32* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc45: ref DeriveFromi32 = deref %p.ref
|
||||
// CHECK:STDOUT: %n.ref: <error> = name_ref n, <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @ConvertToBadBaseTuple(%p: DeriveFromTuple*) -> (Base,)* {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromTuple* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc35_69.1: ref DeriveFromTuple = deref %p.ref
|
||||
// CHECK:STDOUT: %.loc35_69.2: ref (Base,) = class_element_access %.loc35_69.1, element0
|
||||
// CHECK:STDOUT: %.loc35_69.3: (Base,)* = address_of %.loc35_69.2
|
||||
// CHECK:STDOUT: %.loc35_69.4: (Base,)* = converted %p.ref, %.loc35_69.3
|
||||
// CHECK:STDOUT: return %.loc35_69.4
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessMemberWithInvalidBaseTuple(%p: DeriveFromTuple*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromTuple* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc59: ref DeriveFromTuple = deref %p.ref
|
||||
// CHECK:STDOUT: %n.ref: <error> = name_ref n, <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @ConvertToBadBaseStruct(%p: DeriveFromStruct*) -> {.a: i32, .b: i32}* {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromStruct* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc46_82.1: ref DeriveFromStruct = deref %p.ref
|
||||
// CHECK:STDOUT: %.loc46_82.2: ref {.a: i32, .b: i32} = class_element_access %.loc46_82.1, element0
|
||||
// CHECK:STDOUT: %.loc46_82.3: {.a: i32, .b: i32}* = address_of %.loc46_82.2
|
||||
// CHECK:STDOUT: %.loc46_82.4: {.a: i32, .b: i32}* = converted %p.ref, %.loc46_82.3
|
||||
// CHECK:STDOUT: return %.loc46_82.4
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessMemberWithInvalidBaseStruct(%p: DeriveFromStruct*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromStruct* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc76: ref DeriveFromStruct = deref %p.ref
|
||||
// CHECK:STDOUT: %n.ref: <error> = name_ref n, <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @ConvertToBadBaseIncomplete(%p: DeriveFromIncomplete*) -> Incomplete* {
|
||||
@@ -226,13 +324,40 @@ fn ConvertToBadBaseFinal(p: DeriveFromFinal*) -> Final* { return p; }
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessMemberWithInvalidBaseIncomplete(%p: DeriveFromIncomplete*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromIncomplete* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc95: ref DeriveFromIncomplete = deref %p.ref
|
||||
// CHECK:STDOUT: %n.ref: <error> = name_ref n, <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @ConvertToBadBaseFinal(%p: DeriveFromFinal*) -> Final* {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromFinal* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc72_67.1: ref DeriveFromFinal = deref %p.ref
|
||||
// CHECK:STDOUT: %.loc72_67.2: ref Final = class_element_access %.loc72_67.1, element0
|
||||
// CHECK:STDOUT: %.loc72_67.3: Final* = address_of %.loc72_67.2
|
||||
// CHECK:STDOUT: %.loc72_67.4: Final* = converted %p.ref, %.loc72_67.3
|
||||
// CHECK:STDOUT: return %.loc72_67.4
|
||||
// CHECK:STDOUT: %.loc106_11.1: ref DeriveFromFinal = deref %p.ref
|
||||
// CHECK:STDOUT: %.loc106_11.2: ref Final = class_element_access %.loc106_11.1, element0
|
||||
// CHECK:STDOUT: %.loc106_11.3: Final* = address_of %.loc106_11.2
|
||||
// CHECK:STDOUT: %.loc106_11.4: Final* = converted %p.ref, %.loc106_11.3
|
||||
// CHECK:STDOUT: return %.loc106_11.4
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessMemberWithInvalidBaseFinal_WithMember(%p: DeriveFromFinal*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromFinal* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc110_11: ref DeriveFromFinal = deref %p.ref
|
||||
// CHECK:STDOUT: %.loc110_14.1: ref Final = class_element_access %.loc110_11, element0
|
||||
// CHECK:STDOUT: %.loc110_14.2: ref Final = converted %.loc110_11, %.loc110_14.1
|
||||
// CHECK:STDOUT: %.loc110_14.3: ref i32 = class_element_access %.loc110_14.2, element0
|
||||
// CHECK:STDOUT: %.loc110_14.4: i32 = bind_value %.loc110_14.3
|
||||
// CHECK:STDOUT: return %.loc110_14.4
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @AccessMemberWithInvalidBaseFinal_NoMember(%p: DeriveFromFinal*) -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %p.ref: DeriveFromFinal* = name_ref p, %p
|
||||
// CHECK:STDOUT: %.loc117: ref DeriveFromFinal = deref %p.ref
|
||||
// CHECK:STDOUT: %b.ref: <error> = name_ref b, <error>
|
||||
// CHECK:STDOUT: return <error>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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
|
||||
|
||||
base class B {
|
||||
fn F();
|
||||
|
||||
class C {
|
||||
fn F();
|
||||
}
|
||||
}
|
||||
|
||||
class D {
|
||||
extend base: B;
|
||||
}
|
||||
|
||||
// TODO: This should be rejected.
|
||||
fn D.F() {}
|
||||
|
||||
// CHECK:STDERR: fail_base_method_define.carbon:[[@LINE+3]]:6: ERROR: Name `C` not found.
|
||||
// CHECK:STDERR: fn D.C.F() {}
|
||||
// CHECK:STDERR: ^
|
||||
fn D.C.F() {}
|
||||
|
||||
// CHECK:STDOUT: --- fail_base_method_define.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %.loc12_3.1: type = struct_type {}
|
||||
// CHECK:STDOUT: %.loc12_3.2: type = tuple_type ()
|
||||
// CHECK:STDOUT: %.loc7: type = ptr_type {}
|
||||
// CHECK:STDOUT: %.loc17: type = struct_type {.base: B}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.B = %B.decl, .D = %D.decl}
|
||||
// CHECK:STDOUT: %B.decl = class_decl @B, ()
|
||||
// CHECK:STDOUT: %B: type = class_type @B
|
||||
// CHECK:STDOUT: %D.decl = class_decl @D, ()
|
||||
// CHECK:STDOUT: %D: type = class_type @D
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.3
|
||||
// CHECK:STDOUT: %.loc25: <function> = fn_decl @.1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @B {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.1
|
||||
// CHECK:STDOUT: %C.decl = class_decl @C, ()
|
||||
// CHECK:STDOUT: %C: type = class_type @C
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: .C = %C.decl
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @C {
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .F = %F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @D {
|
||||
// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B
|
||||
// CHECK:STDOUT: %.loc16_17.1: type = unbound_element_type D, B
|
||||
// CHECK:STDOUT: %.loc16_17.2: <unbound element of class D> = base_decl B, element0
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc16_17.2
|
||||
// CHECK:STDOUT: .F = file.%F
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.1();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.2();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F.3() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @.1() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -78,6 +78,7 @@ class C4 {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc13_25.2
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @C2 {
|
||||
@@ -96,6 +97,7 @@ class C4 {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc33_25.2
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @C4 {
|
||||
@@ -105,5 +107,6 @@ class C4 {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc43_24.2
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -69,6 +69,7 @@ class D {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc11_18.2
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @D {
|
||||
@@ -79,5 +80,6 @@ class D {
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc23_18.2
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -48,5 +48,6 @@ let b: B = C.base;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc10_17.2
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -83,6 +83,7 @@ fn ConvertIncomplete(p: Incomplete*) -> A2* { return p; }
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .base = %.loc16_18.2
|
||||
// CHECK:STDOUT: .b = %b
|
||||
// CHECK:STDOUT: extend name_scope2
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Incomplete;
|
||||
|
||||
@@ -70,6 +70,7 @@ fn Call(p: Derived*) -> i32 {
|
||||
// CHECK:STDOUT: .base = %.loc12_20.2
|
||||
// CHECK:STDOUT: .SelfBase = %SelfBase
|
||||
// CHECK:STDOUT: .AddrSelfBase = %AddrSelfBase
|
||||
// CHECK:STDOUT: extend name_scope1
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @SelfBase[%self: Base]() -> i32 {
|
||||
|
||||
@@ -28,18 +28,18 @@ library "Bar" impl;
|
||||
// CHECK:STDOUT: --- no_api.impl.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {has_load_error}
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {has_error}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- no_api_lib.impl.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {has_load_error}
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {has_error}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- no_api_main_lib.impl.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {has_load_error}
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {has_error}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ import B;
|
||||
// CHECK:STDOUT: --- c.impl.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {has_load_error}
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {has_error}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- cycle_child.carbon
|
||||
|
||||
@@ -20,7 +20,7 @@ var a: () = A();
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.a = %a, has_load_error}
|
||||
// CHECK:STDOUT: package: <namespace> = namespace {.a = %a, has_error}
|
||||
// CHECK:STDOUT: %.loc7_9.1: () = tuple_literal ()
|
||||
// CHECK:STDOUT: %.loc7_9.2: type = converted %.loc7_9.1, constants.%.loc7
|
||||
// CHECK:STDOUT: %a.var: ref () = var a
|
||||
|
||||
@@ -174,6 +174,7 @@ CARBON_DIAGNOSTIC_KIND(DerefOfNonPointer)
|
||||
CARBON_DIAGNOSTIC_KIND(DerefOfType)
|
||||
CARBON_DIAGNOSTIC_KIND(FunctionPreviousDefinition)
|
||||
CARBON_DIAGNOSTIC_KIND(FunctionRedefinition)
|
||||
CARBON_DIAGNOSTIC_KIND(NameAmbiguousDueToExtend)
|
||||
CARBON_DIAGNOSTIC_KIND(NameNotFound)
|
||||
CARBON_DIAGNOSTIC_KIND(NameDeclDuplicate)
|
||||
CARBON_DIAGNOSTIC_KIND(NameDeclPrevious)
|
||||
|
||||
@@ -609,7 +609,7 @@ class Formatter {
|
||||
out_ << " {\n";
|
||||
FormatCodeBlock(class_info.body_block_id);
|
||||
out_ << "\n!members:";
|
||||
FormatNameScope(class_info.scope_id, "", "\n .");
|
||||
FormatNameScope(class_info.scope_id, "", "\n ");
|
||||
out_ << "\n}\n";
|
||||
} else {
|
||||
out_ << ";\n";
|
||||
@@ -628,7 +628,7 @@ class Formatter {
|
||||
out_ << " {\n";
|
||||
FormatCodeBlock(interface_info.body_block_id);
|
||||
out_ << "\n!members:";
|
||||
FormatNameScope(interface_info.scope_id, "", "\n .");
|
||||
FormatNameScope(interface_info.scope_id, "", "\n ");
|
||||
out_ << "\n}\n";
|
||||
} else {
|
||||
out_ << ";\n";
|
||||
@@ -721,14 +721,19 @@ class Formatter {
|
||||
|
||||
llvm::ListSeparator sep(separator);
|
||||
for (auto [inst_id, name_id] : entries) {
|
||||
out_ << sep << prefix;
|
||||
out_ << sep << prefix << ".";
|
||||
FormatName(name_id);
|
||||
out_ << " = ";
|
||||
FormatInstName(inst_id);
|
||||
}
|
||||
|
||||
if (scope.has_load_error) {
|
||||
out_ << sep << "has_load_error";
|
||||
for (auto extended_scope_id : scope.extended_scopes) {
|
||||
// TODO: Print this scope in a better way.
|
||||
out_ << sep << prefix << "extend " << extended_scope_id;
|
||||
}
|
||||
|
||||
if (scope.has_error) {
|
||||
out_ << sep << prefix << "has_error";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,7 +991,7 @@ class Formatter {
|
||||
|
||||
auto FormatArg(NameScopeId id) -> void {
|
||||
out_ << '{';
|
||||
FormatNameScope(id, ", ", ".");
|
||||
FormatNameScope(id, ", ", "");
|
||||
out_ << '}';
|
||||
}
|
||||
|
||||
|
||||
@@ -168,9 +168,30 @@ struct NameScope {
|
||||
// Names in the scope.
|
||||
llvm::DenseMap<NameId, InstId> names;
|
||||
|
||||
// Whether the scope corresponds to an import that failed. There may still be
|
||||
// names from successful imports, or the current file.
|
||||
bool has_load_error = false;
|
||||
// Scopes extended by this scope.
|
||||
//
|
||||
// TODO: A `NameScopeId` is currently insufficient to describe an extended
|
||||
// scope in general. For example:
|
||||
//
|
||||
// class A(T:! type) {
|
||||
// extend base: B(T*);
|
||||
// }
|
||||
//
|
||||
// needs to describe the `T*` argument.
|
||||
//
|
||||
// Small vector size is set to 1: we expect that there will rarely be more
|
||||
// than a single extended scope. Currently the only kind of extended scope is
|
||||
// a base class, and there can be only one of those per scope.
|
||||
// TODO: Revisit this once we have more kinds of extended scope and data.
|
||||
// TODO: Consider using something like `TinyPtrVector` for this.
|
||||
llvm::SmallVector<NameScopeId, 1> extended_scopes;
|
||||
|
||||
// Whether we have diagnosed an error in a construct that would have added
|
||||
// names to this scope. For example, this can happen if an `import` failed or
|
||||
// an `extend` declaration was ill-formed. If true, the `names` map is
|
||||
// assumed to be missing names as a result of the error, and no further
|
||||
// errors are produced for lookup failures in this scope.
|
||||
bool has_error = false;
|
||||
};
|
||||
|
||||
// Provides a ValueStore wrapper for an API specific to name scopes.
|
||||
|
||||
Reference in New Issue
Block a user