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:
Richard Smith
2023-12-14 19:51:55 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent 23c7d7dd99
commit de0c02ddae
28 changed files with 892 additions and 132 deletions
+3 -3
View File
@@ -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
View File
@@ -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,
+6
View File
@@ -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,
+9 -2
View File
@@ -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(
+8 -2
View File
@@ -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`.
+1 -1
View File
@@ -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:
+86 -30
View File
@@ -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;
}
+3 -2
View File
@@ -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
View File
@@ -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 {
+89
View File
@@ -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:
+90
View File
@@ -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:
+134
View File
@@ -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:
+2
View File
@@ -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* {
+1
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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)
+11 -6
View File
@@ -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_ << '}';
}
+24 -3
View File
@@ -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.