Defer parsing of method bodies until the end of a suitable enclosing scope. (#3832)

In parse, form a list of methods that are defined inline, tracking where
they start, where they end, and which other inline methods are nested
within them.

In check, when we reach an inline method body, skip it and add it to a
worklist to be processed later. We also track when we reach the start
and end of a context in which inline method bodies are deferred, so that
we know when to replay the bodies.

When suspending a function definition to be processed later, the
`DeclNameStack` entry is moved to separate storage, including popping
the corresponding scopes from the scope stack and removing the
corresponding lexical names from lexical lookup. Later, when we return
to the function and parse its definition, the `DeclNameStack` entry is
restored. The same is done when we reach the end of a nested context
that can have inline methods, so that we can reenter the nested scope
before processing its members.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
Richard Smith
2024-04-01 18:25:27 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins
parent d7fb1b287d
commit f9ce0b194d
48 changed files with 1439 additions and 270 deletions
+9
View File
@@ -238,6 +238,15 @@ cc_test(
],
)
cc_library(
name = "variant_helpers",
hdrs = ["variant_helpers.h"],
deps = [
":error",
"@llvm-project//llvm:Support",
],
)
cc_library(
name = "vlog",
srcs = ["vlog_internal.h"],
+42
View File
@@ -0,0 +1,42 @@
// 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
#ifndef CARBON_COMMON_VARIANT_HELPERS_H_
#define CARBON_COMMON_VARIANT_HELPERS_H_
#include <variant>
#include "common/error.h"
#include "llvm/ADT/StringRef.h"
namespace Carbon {
namespace Internal {
// Form an overload set from a list of functions. For example:
//
// ```
// auto overloaded = Overload{[] (int) {}, [] (float) {}};
// ```
template <typename... Fs>
struct Overload : Fs... {
using Fs::operator()...;
};
template <typename... Fs>
Overload(Fs...) -> Overload<Fs...>;
} // namespace Internal
// Pattern-match against the type of the value stored in the variant `V`. Each
// element of `fs` should be a function that takes one or more of the variant
// values in `V`.
template <typename V, typename... Fs>
auto VariantMatch(V&& v, Fs&&... fs) -> decltype(auto) {
return std::visit(Internal::Overload{std::forward<Fs&&>(fs)...},
std::forward<V&&>(v));
}
} // namespace Carbon
#endif // CARBON_COMMON_VARIANT_HELPERS_H_
+6 -1
View File
@@ -101,7 +101,10 @@ cc_library(
cc_library(
name = "check",
srcs = ["check.cpp"] +
srcs = [
"check.cpp",
"handle.h",
] +
# Glob handler files to avoid missing any.
glob([
"handle_*.cpp",
@@ -117,7 +120,9 @@ cc_library(
":operator",
":pointer_dereference",
"//common:check",
"//common:error",
"//common:ostream",
"//common:variant_helpers",
"//toolchain/base:pretty_stack_trace_function",
"//toolchain/base:value_store",
"//toolchain/diagnostics:diagnostic_emitter",
+395 -7
View File
@@ -4,10 +4,15 @@
#include "toolchain/check/check.h"
#include <variant>
#include "common/check.h"
#include "common/error.h"
#include "toolchain/base/pretty_stack_trace_function.h"
#include "toolchain/check/context.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/check/function.h"
#include "toolchain/check/handle.h"
#include "toolchain/check/import.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
@@ -21,11 +26,6 @@
namespace Carbon::Check {
// Parse node handlers. Returns false for unrecoverable errors.
#define CARBON_PARSE_NODE_KIND(Name) \
auto Handle##Name(Context& context, Parse::Name##Id node_id) -> bool;
#include "toolchain/parse/node_kind.def"
// Handles the transformation of a SemIRLoc to a DiagnosticLoc.
//
// TODO: Move this to diagnostic_helpers.cpp.
@@ -274,14 +274,400 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info)
<< "Created an unexpected number of IRs";
}
namespace {
// State used to track the next deferred function definition that we will
// encounter and need to reorder.
class NextDeferredDefinitionCache {
public:
explicit NextDeferredDefinitionCache(const Parse::Tree* tree) : tree_(tree) {
SkipTo(Parse::DeferredDefinitionIndex(0));
}
// Set the specified deferred definition index as being the next one that will
// be encountered.
auto SkipTo(Parse::DeferredDefinitionIndex next_index) -> void {
index_ = next_index;
if (static_cast<std::size_t>(index_.index) ==
tree_->deferred_definitions().size()) {
start_id_ = Parse::NodeId::Invalid;
} else {
start_id_ = tree_->deferred_definitions().Get(index_).start_id;
}
}
// Returns the index of the next deferred definition to be encountered.
auto index() const -> Parse::DeferredDefinitionIndex { return index_; }
// Returns the ID of the start node of the next deferred definition.
auto start_id() const -> Parse::NodeId { return start_id_; }
private:
const Parse::Tree* tree_;
Parse::DeferredDefinitionIndex index_ =
Parse::DeferredDefinitionIndex::Invalid;
Parse::NodeId start_id_ = Parse::NodeId::Invalid;
};
} // namespace
// Determines whether we are currently declaring a name in a scope in which
// function definitions are deferred. When entering another deferred definition
// scope, the inner scope's function definitions are checked at the end of the
// outer scope, not the inner one. For example:
//
// ```
// class A {
// class B {
// fn F() -> A { return {}; }
// }
// } // A.B.F is type-checked here, with A complete.
//
// fn F() {
// class C {
// fn G() {}
// } // C.G is type-checked here.
// }
// ```
static auto IsInDeferredDefinitionScope(Context& context) -> bool {
auto inst_id = context.name_scopes().GetInstIdIfValid(
context.decl_name_stack().PeekTargetScope());
if (!inst_id.is_valid()) {
return false;
}
switch (context.insts().Get(inst_id).kind()) {
case SemIR::ClassDecl::Kind:
case SemIR::ImplDecl::Kind:
case SemIR::InterfaceDecl::Kind:
// TODO: Named constraints, mixins.
return true;
default:
return false;
}
}
// Determines whether this node kind is the start of a deferred definition
// scope.
static auto IsStartOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool {
switch (kind) {
case Parse::NodeKind::ClassDefinitionStart:
case Parse::NodeKind::ImplDefinitionStart:
case Parse::NodeKind::InterfaceDefinitionStart:
case Parse::NodeKind::NamedConstraintDefinitionStart:
// TODO: Mixins.
return true;
default:
return false;
}
}
// Determines whether this node kind is the end of a deferred definition scope.
static auto IsEndOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool {
switch (kind) {
case Parse::NodeKind::ClassDefinition:
case Parse::NodeKind::ImplDefinition:
case Parse::NodeKind::InterfaceDefinition:
case Parse::NodeKind::NamedConstraintDefinition:
// TODO: Mixins.
return true;
default:
return false;
}
}
namespace {
// A worklist of pending tasks to perform to check deferred function definitions
// in the right order.
class DeferredDefinitionWorklist {
public:
// A worklist task that indicates we should check a deferred function
// definition that we previously skipped.
struct CheckSkippedDefinition {
// The definition that we skipped.
Parse::DeferredDefinitionIndex definition_index;
// The suspended function.
SuspendedFunction suspended_fn;
};
// A worklist task that indicates we should enter a nested deferred definition
// scope.
struct EnterDeferredDefinitionScope {
// The suspended scope. This is only set once we reach the end of the scope.
std::optional<DeclNameStack::SuspendedName> suspended_name;
// Whether this scope is itself within an outer deferred definition scope.
// If so, we'll delay processing its contents until we reach the end of the
// enclosing scope.
bool in_deferred_definition_scope;
};
// A worklist task that indicates we should leave a deferred definition scope.
struct LeaveDeferredDefinitionScope {
// Whether this scope is within another deferred definition scope.
bool in_deferred_definition_scope;
};
// A pending type-checking task.
using Task =
std::variant<CheckSkippedDefinition, EnterDeferredDefinitionScope,
LeaveDeferredDefinitionScope>;
DeferredDefinitionWorklist() {
// See declaration of `worklist_`.
worklist_.reserve(64);
}
// Suspend the current function definition and push a task onto the worklist
// to finish it later.
auto SuspendFunctionAndPush(Context& context,
Parse::DeferredDefinitionIndex index,
Parse::FunctionDefinitionStartId node_id)
-> void {
worklist_.push_back(CheckSkippedDefinition{
index, HandleFunctionDefinitionSuspend(context, node_id)});
}
// Push a task to re-enter a function scope, so that functions defined within
// it are type-checked in the right context.
auto PushEnterDeferredDefinitionScope(Context& context) -> void {
enclosing_scopes_.push_back(worklist_.size());
worklist_.push_back(EnterDeferredDefinitionScope{
std::nullopt, IsInDeferredDefinitionScope(context)});
}
// Suspend the current deferred definition scope, which is finished but still
// on the decl_name_stack, and push a task to leave the scope when we're
// type-checking deferred definitions. Returns `true` if the current list of
// deferred definitions should be type-checked immediately.
auto SuspendFinishedScopeAndPush(Context& context) -> bool;
// Pop the next task off the worklist.
auto Pop() -> Task { return worklist_.pop_back_val(); }
// CHECK that the work list has no further work.
auto VerifyEmpty() {
CARBON_CHECK(worklist_.empty() && enclosing_scopes_.empty())
<< "Tasks left behind on worklist.";
}
private:
// A worklist of type-checking tasks we'll need to do later.
//
// Don't allocate any inline storage here. A Task is fairly large, so we never
// want this to live on the stack. Instead, we reserve space in the
// constructor for a fairly large number of deferred definitions.
llvm::SmallVector<Task, 0> worklist_;
// Indexes in `worklist` of deferred definition scopes that are currently
// still open.
llvm::SmallVector<size_t> enclosing_scopes_;
};
} // namespace
auto DeferredDefinitionWorklist::SuspendFinishedScopeAndPush(Context& context)
-> bool {
auto scope_index = enclosing_scopes_.pop_back_val();
// If we've not found any deferred definitions in this scope, clean up the
// stack.
if (scope_index == worklist_.size() - 1) {
context.decl_name_stack().PopScope();
worklist_.pop_back();
return false;
}
// If we're finishing a nested deferred definition scope, keep track of that
// but don't type-check deferred definitions now.
auto& enter_scope = get<EnterDeferredDefinitionScope>(worklist_[scope_index]);
if (enter_scope.in_deferred_definition_scope) {
// This is a nested deferred definition scope. Suspend the inner scope so we
// can restore it when we come to type-check the deferred definitions.
enter_scope.suspended_name = context.decl_name_stack().Suspend();
// Enqueue a task to leave the nested scope.
worklist_.push_back(
LeaveDeferredDefinitionScope{.in_deferred_definition_scope = true});
return false;
}
// We're at the end of a non-nested deferred definition scope. Prepare to
// start checking deferred definitions. Enqueue a task to leave this outer
// scope and end checking deferred definitions.
worklist_.push_back(
LeaveDeferredDefinitionScope{.in_deferred_definition_scope = false});
// We'll process the worklist in reverse index order, so reverse the part of
// it we're about to execute so we run our tasks in the order in which they
// were pushed.
std::reverse(worklist_.begin() + scope_index, worklist_.end());
// Pop the `EnterDeferredDefinitionScope` that's now on the end of the
// worklist. We stay in that scope rather than suspending then immediately
// resuming it.
CARBON_CHECK(
holds_alternative<EnterDeferredDefinitionScope>(worklist_.back()))
<< "Unexpected task in worklist.";
worklist_.pop_back();
return true;
}
namespace {
// A traversal of the node IDs in the parse tree, in the order in which we need
// to check them.
class NodeIdTraversal {
public:
explicit NodeIdTraversal(Context& context)
: context_(context), next_deferred_definition_(&context.parse_tree()) {
chunks_.push_back(
{.it = context.parse_tree().postorder().begin(),
.end = context.parse_tree().postorder().end(),
.next_definition = Parse::DeferredDefinitionIndex::Invalid});
}
// Finds the next `NodeId` to type-check. Returns nullopt if the traversal is
// complete.
auto Next() -> std::optional<Parse::NodeId>;
// Performs any processing necessary after we type-check a node.
auto Handle(Parse::NodeKind parse_kind) -> void {
// When we reach the start of a deferred definition scope, add a task to the
// worklist to check future skipped definitions in the new context.
if (IsStartOfDeferredDefinitionScope(parse_kind)) {
worklist_.PushEnterDeferredDefinitionScope(context_);
}
// When we reach the end of a deferred definition scope, add a task to the
// worklist to leave the scope. If this is not a nested scope, start
// checking the deferred definitions now.
if (IsEndOfDeferredDefinitionScope(parse_kind)) {
chunks_.back().checking_deferred_definitions =
worklist_.SuspendFinishedScopeAndPush(context_);
}
}
private:
// A chunk of the parse tree that we need to type-check.
struct Chunk {
Parse::Tree::PostorderIterator it;
Parse::Tree::PostorderIterator end;
// The next definition that will be encountered after this chunk completes.
Parse::DeferredDefinitionIndex next_definition;
// Whether we are currently checking deferred definitions, rather than the
// tokens of this chunk. If so, we'll pull tasks off `worklist` and execute
// them until we're done with this batch of deferred definitions. Otherwise,
// we'll pull node IDs from `*it` until it reaches `end`.
bool checking_deferred_definitions = false;
};
// Re-enter a nested deferred definition scope.
auto PerformTask(
DeferredDefinitionWorklist::EnterDeferredDefinitionScope&& enter)
-> void {
CARBON_CHECK(enter.suspended_name)
<< "Entering a scope with no suspension information.";
context_.decl_name_stack().Restore(std::move(*enter.suspended_name));
}
// Leave a nested or top-level deferred definition scope.
auto PerformTask(
DeferredDefinitionWorklist::LeaveDeferredDefinitionScope&& leave)
-> void {
if (!leave.in_deferred_definition_scope) {
// We're done with checking deferred definitions.
chunks_.back().checking_deferred_definitions = false;
}
context_.decl_name_stack().PopScope();
}
// Resume checking a deferred definition.
auto PerformTask(
DeferredDefinitionWorklist::CheckSkippedDefinition&& parse_definition)
-> void {
auto& [definition_index, suspended_fn] = parse_definition;
const auto& definition_info =
context_.parse_tree().deferred_definitions().Get(definition_index);
HandleFunctionDefinitionResume(context_, definition_info.start_id,
std::move(suspended_fn));
chunks_.push_back(
{.it = context_.parse_tree().postorder(definition_info.start_id).end(),
.end = context_.parse_tree()
.postorder(definition_info.definition_id)
.end(),
.next_definition = next_deferred_definition_.index()});
++definition_index.index;
next_deferred_definition_.SkipTo(definition_index);
}
Context& context_;
NextDeferredDefinitionCache next_deferred_definition_;
DeferredDefinitionWorklist worklist_;
llvm::SmallVector<Chunk> chunks_;
};
} // namespace
auto NodeIdTraversal::Next() -> std::optional<Parse::NodeId> {
while (true) {
// If we're checking deferred definitions, find the next definition we
// should check, restore its suspended state, and add a corresponding
// `Chunk` to the top of the chunk list.
if (chunks_.back().checking_deferred_definitions) {
std::visit(
[&](auto&& task) { PerformTask(std::forward<decltype(task)>(task)); },
worklist_.Pop());
continue;
}
// If we're not checking deferred definitions, produce the next parse node
// for this chunk. If we've run out of parse nodes, we're done with this
// chunk of the parse tree.
if (chunks_.back().it == chunks_.back().end) {
auto old_chunk = chunks_.pop_back_val();
// If we're out of chunks, then we're done entirely.
if (chunks_.empty()) {
worklist_.VerifyEmpty();
return std::nullopt;
}
next_deferred_definition_.SkipTo(old_chunk.next_definition);
continue;
}
auto node_id = *chunks_.back().it;
// If we've reached the start of a deferred definition, skip to the end of
// it, and track that we need to check it later.
if (node_id == next_deferred_definition_.start_id()) {
const auto& definition_info =
context_.parse_tree().deferred_definitions().Get(
next_deferred_definition_.index());
worklist_.SuspendFunctionAndPush(context_,
next_deferred_definition_.index(),
definition_info.start_id);
// Continue type-checking the parse tree after the end of the definition.
chunks_.back().it =
context_.parse_tree().postorder(definition_info.definition_id).end();
next_deferred_definition_.SkipTo(definition_info.next_definition_index);
continue;
}
++chunks_.back().it;
return node_id;
}
}
// Loops over all nodes in the tree. On some errors, this may return early,
// for example if an unrecoverable state is encountered.
// NOLINTNEXTLINE(readability-function-size)
static auto ProcessNodeIds(Context& context,
ErrorTrackingDiagnosticConsumer& err_tracker)
-> bool {
for (auto node_id : context.parse_tree().postorder()) {
switch (auto parse_kind = context.parse_tree().node_kind(node_id)) {
NodeIdTraversal traversal(context);
while (auto maybe_node_id = traversal.Next()) {
auto node_id = *maybe_node_id;
auto parse_kind = context.parse_tree().node_kind(node_id);
switch (parse_kind) {
#define CARBON_PARSE_NODE_KIND(Name) \
case Parse::NodeKind::Name: { \
if (!Check::Handle##Name(context, Parse::Name##Id(node_id))) { \
@@ -293,6 +679,8 @@ static auto ProcessNodeIds(Context& context,
}
#include "toolchain/parse/node_kind.def"
}
traversal.Handle(parse_kind);
}
return true;
}
+30
View File
@@ -63,6 +63,36 @@ auto DeclNameStack::PopScope() -> void {
decl_name_stack_.pop_back();
}
auto DeclNameStack::Suspend() -> SuspendedName {
CARBON_CHECK(decl_name_stack_.back().state == NameContext::State::Finished)
<< "Missing call to FinishName before Suspend";
SuspendedName result = {decl_name_stack_.pop_back_val(), {}};
auto enclosing_index = result.name_context.enclosing_scope;
auto& scope_stack = context_->scope_stack();
while (scope_stack.PeekIndex() > enclosing_index) {
result.scopes.push_back(scope_stack.Suspend());
}
CARBON_CHECK(scope_stack.PeekIndex() == enclosing_index)
<< "Scope index " << enclosing_index
<< " does not enclose the current scope " << scope_stack.PeekIndex();
return result;
}
auto DeclNameStack::Restore(SuspendedName sus) -> void {
// The enclosing state must be the same when a name is restored.
CARBON_CHECK(context_->scope_stack().PeekIndex() ==
sus.name_context.enclosing_scope)
<< "Name restored at the wrong position in the name stack.";
// clang-tidy warns that the `std::move` below has no effect. While that's
// true, this `move` defends against `NameContext` growing more state later.
// NOLINTNEXTLINE(performance-move-const-arg)
decl_name_stack_.push_back(std::move(sus.name_context));
for (auto& suspended_scope : llvm::reverse(sus.scopes)) {
context_->scope_stack().Restore(std::move(suspended_scope));
}
}
auto DeclNameStack::LookupOrAddName(NameContext name_context,
SemIR::InstId target_id) -> SemIR::InstId {
switch (name_context.state) {
+23
View File
@@ -7,6 +7,7 @@
#include "llvm/ADT/SmallVector.h"
#include "toolchain/check/scope_index.h"
#include "toolchain/check/scope_stack.h"
#include "toolchain/sem_ir/ids.h"
namespace Carbon::Check {
@@ -136,6 +137,21 @@ class DeclNameStack {
};
};
// Information about a declaration name that has been temporarily removed from
// the stack and will later be restored. Names can only be suspended once they
// are finished.
struct SuspendedName {
// The declaration name information.
NameContext name_context;
// Suspended scopes. We only preallocate space for two of these, because
// suspended names are usually used for classes and functions with
// unqualified names, which only need at most two scopes -- one scope for
// the parameter and one scope for the entity itself, and we can store quite
// a few of these when processing a large class definition.
llvm::SmallVector<ScopeStack::SuspendedScope, 2> scopes;
};
explicit DeclNameStack(Context* context) : context_(context) {}
// Pushes processing of a new declaration name, which will be used
@@ -172,6 +188,13 @@ class DeclNameStack {
// This should be called at the end of the declaration.
auto PopScope() -> void;
// Temporarily remove the current declaration name and its associated scopes
// from the stack. Can only be called once the name is finished.
auto Suspend() -> SuspendedName;
// Restore a previously suspended name.
auto Restore(SuspendedName sus) -> void;
// Creates and returns a name context corresponding to declaring an
// unqualified name in the current context. This is suitable for adding to
// name lookup in situations where a qualified name is not permitted, such as
+14
View File
@@ -6,12 +6,26 @@
#define CARBON_TOOLCHAIN_CHECK_FUNCTION_H_
#include "toolchain/check/context.h"
#include "toolchain/check/decl_name_stack.h"
#include "toolchain/check/subst.h"
#include "toolchain/sem_ir/function.h"
#include "toolchain/sem_ir/ids.h"
namespace Carbon::Check {
// State saved for a function definition that has been suspended after
// processing its declaration and before processing its body. This is used for
// inline method handling.
struct SuspendedFunction {
// The function that was declared.
SemIR::FunctionId function_id;
// The instruction ID of the FunctionDecl instruction.
SemIR::InstId decl_id;
// The declaration name information of the function. This includes the scope
// information, such as parameter names.
DeclNameStack::SuspendedName saved_name_state;
};
// Checks that `new_function_id` has the same parameter types and return type as
// `prev_function_id`, applying the specified set of substitutions to the
// previous function. Prints a suitable diagnostic and returns false if not.
+35
View File
@@ -0,0 +1,35 @@
// 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
#ifndef CARBON_TOOLCHAIN_CHECK_HANDLE_H_
#define CARBON_TOOLCHAIN_CHECK_HANDLE_H_
#include "toolchain/check/context.h"
#include "toolchain/check/function.h"
#include "toolchain/parse/node_ids.h"
namespace Carbon::Check {
// Parse node handlers. Returns false for unrecoverable errors.
#define CARBON_PARSE_NODE_KIND(Name) \
auto Handle##Name(Context& context, Parse::Name##Id node_id) -> bool;
#include "toolchain/parse/node_kind.def"
// Handle suspending the definition of a function. This is used for inline
// methods, which are processed out of the normal lexical order. This plus
// HandleFunctionDefinitionResume carry out the same actions as
// HandleFunctionDefinitionStart, except that the various context stacks are
// cleared out in between.
auto HandleFunctionDefinitionSuspend(Context& context,
Parse::FunctionDefinitionStartId node_id)
-> SuspendedFunction;
// Handle resuming the definition of a function, after a previous suspension.
auto HandleFunctionDefinitionResume(Context& context,
Parse::FunctionDefinitionStartId node_id,
SuspendedFunction suspended_fn) -> void;
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_HANDLE_H_
+2 -2
View File
@@ -332,12 +332,12 @@ auto HandleClassDefinition(Context& context,
auto class_id =
context.node_stack().Pop<Parse::NodeKind::ClassDefinitionStart>();
context.inst_block_stack().Pop();
context.scope_stack().Pop();
context.decl_name_stack().PopScope();
// The class type is now fully defined.
auto& class_info = context.classes().Get(class_id);
class_info.object_repr_id = context.GetStructType(fields_id);
// The decl_name_stack and scopes are popped by `ProcessNodeIds`.
return true;
}
+34 -7
View File
@@ -225,12 +225,12 @@ auto HandleFunctionDecl(Context& context, Parse::FunctionDeclId node_id)
return true;
}
auto HandleFunctionDefinitionStart(Context& context,
Parse::FunctionDefinitionStartId node_id)
-> bool {
// Process the declaration portion of the function.
auto [function_id, decl_id] =
BuildFunctionDecl(context, node_id, /*is_definition=*/true);
// Processes a function definition after a signature for which we have already
// built a function ID. This logic is shared between processing regular function
// definitions and delayed parsing of inline method definitions.
static auto HandleFunctionDefinitionAfterSignature(
Context& context, Parse::FunctionDefinitionStartId node_id,
SemIR::FunctionId function_id, SemIR::InstId decl_id) -> void {
auto& function = context.functions().Get(function_id);
// Create the function scope and the entry block.
@@ -239,7 +239,7 @@ auto HandleFunctionDefinitionStart(Context& context,
context.scope_stack().Push(decl_id);
context.AddCurrentCodeBlockToFunction();
// Bring the implicit and explicit parameters into scope.
// Check the parameter types are complete.
for (auto param_id : llvm::concat<SemIR::InstId>(
context.inst_blocks().Get(function.implicit_param_refs_id),
context.inst_blocks().Get(function.param_refs_id))) {
@@ -264,6 +264,33 @@ auto HandleFunctionDefinitionStart(Context& context,
}
context.node_stack().Push(node_id, function_id);
}
auto HandleFunctionDefinitionSuspend(Context& context,
Parse::FunctionDefinitionStartId node_id)
-> SuspendedFunction {
// Process the declaration portion of the function.
auto [function_id, decl_id] =
BuildFunctionDecl(context, node_id, /*is_definition=*/true);
return {function_id, decl_id, context.decl_name_stack().Suspend()};
}
auto HandleFunctionDefinitionResume(Context& context,
Parse::FunctionDefinitionStartId node_id,
SuspendedFunction sus_fn) -> void {
context.decl_name_stack().Restore(sus_fn.saved_name_state);
HandleFunctionDefinitionAfterSignature(context, node_id, sus_fn.function_id,
sus_fn.decl_id);
}
auto HandleFunctionDefinitionStart(Context& context,
Parse::FunctionDefinitionStartId node_id)
-> bool {
// Process the declaration portion of the function.
auto [function_id, decl_id] =
BuildFunctionDecl(context, node_id, /*is_definition=*/true);
HandleFunctionDefinitionAfterSignature(context, node_id, function_id,
decl_id);
return true;
}
+1 -1
View File
@@ -283,7 +283,7 @@ auto HandleImplDefinition(Context& context, Parse::ImplDefinitionId /*node_id*/)
}
context.inst_block_stack().Pop();
context.decl_name_stack().PopScope();
// The decl_name_stack and scopes are popped by `ProcessNodeIds`.
return true;
}
+1 -2
View File
@@ -173,8 +173,6 @@ auto HandleInterfaceDefinition(Context& context,
auto interface_id =
context.node_stack().Pop<Parse::NodeKind::InterfaceDefinitionStart>();
context.inst_block_stack().Pop();
context.scope_stack().Pop();
context.decl_name_stack().PopScope();
auto associated_entities_id = context.args_type_info_stack().Pop();
// The interface type is now fully defined.
@@ -182,6 +180,7 @@ auto HandleInterfaceDefinition(Context& context,
if (!interface_info.associated_entities_id.is_valid()) {
interface_info.associated_entities_id = associated_entities_id;
}
// The decl_name_stack and scopes are popped by `ProcessNodeIds`.
return true;
}
@@ -14,6 +14,7 @@ auto HandleNamedConstraintDecl(Context& context,
auto HandleNamedConstraintDefinition(Context& context,
Parse::NamedConstraintDefinitionId node_id)
-> bool {
// Note that the decl_name_stack will be popped by `ProcessNodeIds`.
return context.TODO(node_id, "HandleNamedConstraintDefinition");
}
+32 -1
View File
@@ -28,12 +28,21 @@ class LexicalLookup {
ScopeIndex scope_index;
};
// A lookup result that has been temporarily removed from scope.
struct SuspendedResult {
// The lookup index. This is notionally a size_t, but is stored in 32 bits
// to keep this type small, which helps to keep SuspendedFunctions small.
uint32_t index;
// The lookup result.
SemIR::InstId inst_id;
};
explicit LexicalLookup(const StringStoreWrapper<IdentifierId>& identifiers)
: lookup_(identifiers.size() + SemIR::NameId::NonIndexValueCount) {}
// Returns the lexical lookup results for a name.
auto Get(SemIR::NameId name_id) -> llvm::SmallVector<Result, 2>& {
size_t index = name_id.index + SemIR::NameId::NonIndexValueCount;
auto index = GetLookupIndex(name_id);
CARBON_CHECK(index < lookup_.size())
<< "An identifier was added after the Context was initialized. "
"Currently, we expect that new identifiers will never be used with "
@@ -44,7 +53,29 @@ class LexicalLookup {
return lookup_[index];
}
// Temporarily remove the top lookup result for `name_id` from scope.
auto Suspend(SemIR::NameId name_id) -> SuspendedResult {
auto index = GetLookupIndex(name_id);
auto& results = lookup_[index];
CARBON_CHECK(!results.empty())
<< "Suspending a nonexistent result for " << name_id << ".";
CARBON_CHECK(index <= std::numeric_limits<uint32_t>::max())
<< "Unexpectedly large index " << index << " for name ID";
return {static_cast<uint32_t>(index), results.pop_back_val().inst_id};
}
// Restore a previously-suspended lookup result.
auto Restore(SuspendedResult sus, ScopeIndex index) -> void {
lookup_[sus.index].push_back({sus.inst_id, index});
}
private:
// Get the index at which the specified name is stored in `lookup_`.
auto GetLookupIndex(SemIR::NameId name_id) -> size_t {
return static_cast<ssize_t>(name_id.index) +
SemIR::NameId::NonIndexValueCount;
}
// Maps identifiers to name lookup results.
// TODO: Consider TinyPtrVector<Result> or similar. For now, use a small size
// of 2 to cover the common case.
+34 -7
View File
@@ -19,13 +19,12 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id,
{.index = next_scope_index_,
.scope_inst_id = scope_inst_id,
.scope_id = scope_id,
.prev_lexical_lookup_has_load_error = lexical_lookup_has_load_error_});
.lexical_lookup_has_load_error =
LexicalLookupHasLoadError() || lexical_lookup_has_load_error});
if (scope_id.is_valid()) {
non_lexical_scope_stack_.push_back({next_scope_index_, scope_id});
}
lexical_lookup_has_load_error_ |= lexical_lookup_has_load_error;
// TODO: Handle this case more gracefully.
CARBON_CHECK(next_scope_index_.index != std::numeric_limits<int32_t>::max())
<< "Ran out of scopes";
@@ -35,8 +34,6 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id,
auto ScopeStack::Pop() -> void {
auto scope = scope_stack_.pop_back_val();
lexical_lookup_has_load_error_ = scope.prev_lexical_lookup_has_load_error;
for (const auto& str_id : scope.names) {
auto& lexical_results = lexical_lookup_.Get(str_id);
CARBON_CHECK(lexical_results.back().scope_index == scope.index)
@@ -88,8 +85,8 @@ auto ScopeStack::LookupInEnclosingScopes(SemIR::NameId name_id)
// If we have no lexical results, check all non-lexical scopes.
if (lexical_results.empty()) {
return {lexical_lookup_has_load_error_ ? SemIR::InstId::BuiltinError
: SemIR::InstId::Invalid,
return {LexicalLookupHasLoadError() ? SemIR::InstId::BuiltinError
: SemIR::InstId::Invalid,
non_lexical_scope_stack_};
}
@@ -142,4 +139,34 @@ auto ScopeStack::SetReturnedVarOrGetExisting(SemIR::InstId inst_id)
return SemIR::InstId::Invalid;
}
auto ScopeStack::Suspend() -> SuspendedScope {
CARBON_CHECK(!scope_stack_.empty()) << "No scope to suspend";
SuspendedScope result = {scope_stack_.pop_back_val(), {}};
if (result.entry.scope_id.is_valid()) {
non_lexical_scope_stack_.pop_back();
}
for (auto name_id : result.entry.names) {
result.suspended_lookups.push_back(lexical_lookup_.Suspend(name_id));
}
// This would be easy to support if we had a need, but currently we do not.
CARBON_CHECK(!result.entry.has_returned_var)
<< "Should not suspend a scope with a returned var.";
return result;
}
auto ScopeStack::Restore(SuspendedScope scope) -> void {
for (auto entry : scope.suspended_lookups) {
// clang-tidy warns that the `std::move` below has no effect. While that's
// true, this `move` defends against the suspended lookup growing more state
// later.
// NOLINTNEXTLINE(performance-move-const-arg)
lexical_lookup_.Restore(std::move(entry), scope.entry.index);
}
if (scope.entry.scope_id.is_valid()) {
non_lexical_scope_stack_.push_back(
{scope.entry.index, scope.entry.scope_id});
}
scope_stack_.push_back(std::move(scope.entry));
}
} // namespace Carbon::Check
+30 -9
View File
@@ -47,6 +47,9 @@ class ScopeStack {
SemIR::NameScopeId name_scope_id;
};
// Information about a scope that has been temporarily removed from the stack.
struct SuspendedScope;
// Pushes a scope onto scope_stack_. NameScopeId::Invalid is used for new
// scopes. lexical_lookup_has_load_error is used to limit diagnostics when a
// given namespace may contain a mix of both successful and failed name
@@ -112,6 +115,12 @@ class ScopeStack {
auto LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id)
-> SemIR::InstId;
// Temporarily removes the top of the stack and its lexical lookup results.
auto Suspend() -> SuspendedScope;
// Restores a suspended scope stack entry.
auto Restore(SuspendedScope scope) -> void;
// Runs verification that the processing cleanly finished.
auto VerifyOnFinish() -> void;
@@ -140,22 +149,29 @@ class ScopeStack {
// The name scope associated with this entry, if any.
SemIR::NameScopeId scope_id;
// The previous state of lexical_lookup_has_load_error_, restored on pop.
bool prev_lexical_lookup_has_load_error;
// Names which are registered with lexical_lookup_, and will need to be
// unregistered when the scope ends.
llvm::DenseSet<SemIR::NameId> names;
// Whether lexical_lookup_ has load errors from this scope or an enclosing
// scope.
bool lexical_lookup_has_load_error;
// Whether a `returned var` was introduced in this scope, and needs to be
// unregistered when the scope ends.
bool has_returned_var = false;
// Names which are registered with lexical_lookup_, and will need to be
// unregistered when the scope ends.
llvm::DenseSet<SemIR::NameId> names;
// TODO: This likely needs to track things which need to be destructed.
};
auto Peek() const -> const ScopeStackEntry& { return scope_stack_.back(); }
// Returns whether lexical lookup currently has any load errors.
auto LexicalLookupHasLoadError() const -> bool {
return !scope_stack_.empty() &&
scope_stack_.back().lexical_lookup_has_load_error;
}
// A stack of scopes from which we can `return`.
llvm::SmallVector<ReturnScope> return_scope_stack_;
@@ -175,10 +191,15 @@ class ScopeStack {
// Tracks lexical lookup results.
LexicalLookup lexical_lookup_;
};
// Whether lexical_lookup_ has load errors, updated whenever scope_stack_ is
// pushed or popped.
bool lexical_lookup_has_load_error_ = false;
struct ScopeStack::SuspendedScope {
// The suspended scope stack entry.
ScopeStackEntry entry;
// The lexical lookups for the suspended entry. The inline size is an attempt
// to keep the size of a `SuspendedFunction` reasonable while avoiding heap
// allocations most of the time.
llvm::SmallVector<LexicalLookup::SuspendedResult, 8> suspended_lookups;
};
} // namespace Carbon::Check
@@ -0,0 +1,52 @@
// 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
class C {
fn F(c: C) -> i32 { return c.a; }
var a: i32;
}
// CHECK:STDOUT: --- complete_in_member_fn.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: %.1: type = unbound_element_type C, i32 [template]
// CHECK:STDOUT: %.2: type = struct_type {.a: i32} [template]
// CHECK:STDOUT: %.3: type = ptr_type {.a: i32} [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: %F: <function> = fn_decl @F [template] {
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %c.loc8_8.1: C = param c
// CHECK:STDOUT: %c.loc8_8.2: C = bind_name c, %c.loc8_8.1
// CHECK:STDOUT: %return.var: ref i32 = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc10: <unbound element of class C> = field_decl a, element0 [template]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: .F = %F
// CHECK:STDOUT: .a = %.loc10
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F(@C.%c.loc8_8.2: C) -> i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %c.ref: C = name_ref c, @C.%c.loc8_8.2
// CHECK:STDOUT: %a.ref: <unbound element of class C> = name_ref a, @C.%.loc10 [template = @C.%.loc10]
// CHECK:STDOUT: %.loc8_31.1: ref i32 = class_element_access %c.ref, element0
// CHECK:STDOUT: %.loc8_31.2: i32 = bind_value %.loc8_31.1
// CHECK:STDOUT: return %.loc8_31.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
+3 -3
View File
@@ -21,8 +21,8 @@ fn G() -> i32 {
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %Class: type = class_type @Class [template]
// CHECK:STDOUT: %.1: i32 = int_literal 1 [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.1: type = struct_type {} [template]
// CHECK:STDOUT: %.2: i32 = int_literal 1 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -48,7 +48,7 @@ fn G() -> i32 {
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() -> i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc9: i32 = int_literal 1 [template = constants.%.1]
// CHECK:STDOUT: %.loc9: i32 = int_literal 1 [template = constants.%.2]
// CHECK:STDOUT: return %.loc9
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -0,0 +1,45 @@
// 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
// TODO: Support parsing classes at function scope.
class A {
fn F() {
// CHECK:STDERR: fail_todo_local_class.carbon:[[@LINE+7]]:5: ERROR: Expected expression.
// CHECK:STDERR: class B {
// CHECK:STDERR: ^~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_local_class.carbon:[[@LINE+3]]:5: ERROR: Semantics TODO: `HandleInvalidParse`.
// CHECK:STDERR: class B {
// CHECK:STDERR: ^~~~~
class B {
fn G() {
var b: B = {};
}
}
}
}
// CHECK:STDOUT: --- fail_todo_local_class.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %A: type = class_type @A [template]
// CHECK:STDOUT: %.1: type = struct_type {} [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {}
// CHECK:STDOUT:
// CHECK:STDOUT: class @A {
// CHECK:STDOUT: %F: <function> = fn_decl @F [template] {}
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%A
// CHECK:STDOUT: .F = %F
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: }
// CHECK:STDOUT:
+141 -84
View File
@@ -5,10 +5,28 @@
// AUTOUPDATE
class Outer {
fn F() {
// Outer and Inner are both complete here.
var o: Outer;
var i: Inner;
}
class Inner {
var pi: Self*;
var po: Outer*;
var qi: Inner*;
fn G() {
// Outer and Inner are both complete here.
var o: Outer;
var i: Inner;
}
}
fn H() {
// Outer and Inner are both complete here.
var o: Outer;
var i: Inner;
}
var po: Self*;
@@ -19,12 +37,12 @@ class Outer {
fn F(a: Outer*) {
let b: Outer.Inner* = (*a).pi;
(*a).po = a;
(*a).qo = a;
(*a).pi = (*a).pi;
(*b).po = a;
(*b).pi = (*a).pi;
(*b).qi = (*a).pi;
a->po = a;
a->qo = a;
a->pi = a->pi;
b->po = a;
b->pi = a->pi;
b->qi = a->pi;
}
// CHECK:STDOUT: --- nested.carbon
@@ -50,111 +68,150 @@ fn F(a: Outer*) {
// CHECK:STDOUT: .F = %F
// CHECK:STDOUT: }
// CHECK:STDOUT: %Outer.decl: type = class_decl @Outer [template = constants.%Outer] {}
// CHECK:STDOUT: %F: <function> = fn_decl @F [template] {
// CHECK:STDOUT: %F: <function> = fn_decl @F.2 [template] {
// CHECK:STDOUT: %Outer.ref: type = name_ref Outer, %Outer.decl [template = constants.%Outer]
// CHECK:STDOUT: %.loc19: type = ptr_type Outer [template = constants.%.3]
// CHECK:STDOUT: %a.loc19_6.1: Outer* = param a
// CHECK:STDOUT: @F.%a: Outer* = bind_name a, %a.loc19_6.1
// CHECK:STDOUT: %.loc37: type = ptr_type Outer [template = constants.%.3]
// CHECK:STDOUT: %a.loc37_6.1: Outer* = param a
// CHECK:STDOUT: @F.2.%a: Outer* = bind_name a, %a.loc37_6.1
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Outer {
// CHECK:STDOUT: %F: <function> = fn_decl @F.1 [template] {}
// CHECK:STDOUT: %Inner.decl: type = class_decl @Inner [template = constants.%Inner] {}
// CHECK:STDOUT: %H: <function> = fn_decl @H [template] {}
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Outer [template = constants.%Outer]
// CHECK:STDOUT: %.loc14_15: type = ptr_type Outer [template = constants.%.3]
// CHECK:STDOUT: %.loc14_9: <unbound element of class Outer> = field_decl po, element0 [template]
// CHECK:STDOUT: %.loc32_15: type = ptr_type Outer [template = constants.%.3]
// CHECK:STDOUT: %.loc32_9: <unbound element of class Outer> = field_decl po, element0 [template]
// CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [template = constants.%Outer]
// CHECK:STDOUT: %.loc15_16: type = ptr_type Outer [template = constants.%.3]
// CHECK:STDOUT: %.loc15_9: <unbound element of class Outer> = field_decl qo, element1 [template]
// CHECK:STDOUT: %.loc33_16: type = ptr_type Outer [template = constants.%.3]
// CHECK:STDOUT: %.loc33_9: <unbound element of class Outer> = field_decl qo, element1 [template]
// CHECK:STDOUT: %Inner.ref: type = name_ref Inner, %Inner.decl [template = constants.%Inner]
// CHECK:STDOUT: %.loc16_16: type = ptr_type Inner [template = constants.%.1]
// CHECK:STDOUT: %.loc16_9: <unbound element of class Outer> = field_decl pi, element2 [template]
// CHECK:STDOUT: %.loc34_16: type = ptr_type Inner [template = constants.%.1]
// CHECK:STDOUT: %.loc34_9: <unbound element of class Outer> = field_decl pi, element2 [template]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Outer
// CHECK:STDOUT: .F = %F
// CHECK:STDOUT: .Inner = %Inner.decl
// CHECK:STDOUT: .po = %.loc14_9
// CHECK:STDOUT: .qo = %.loc15_9
// CHECK:STDOUT: .pi = %.loc16_9
// CHECK:STDOUT: .H = %H
// CHECK:STDOUT: .po = %.loc32_9
// CHECK:STDOUT: .qo = %.loc33_9
// CHECK:STDOUT: .pi = %.loc34_9
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Inner {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Inner [template = constants.%Inner]
// CHECK:STDOUT: %.loc9_17: type = ptr_type Inner [template = constants.%.1]
// CHECK:STDOUT: %.loc9_11: <unbound element of class Inner> = field_decl pi, element0 [template]
// CHECK:STDOUT: %.loc15_17: type = ptr_type Inner [template = constants.%.1]
// CHECK:STDOUT: %.loc15_11: <unbound element of class Inner> = field_decl pi, element0 [template]
// CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [template = constants.%Outer]
// CHECK:STDOUT: %.loc10_18: type = ptr_type Outer [template = constants.%.3]
// CHECK:STDOUT: %.loc10_11: <unbound element of class Inner> = field_decl po, element1 [template]
// CHECK:STDOUT: %.loc16_18: type = ptr_type Outer [template = constants.%.3]
// CHECK:STDOUT: %.loc16_11: <unbound element of class Inner> = field_decl po, element1 [template]
// CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [template = constants.%Inner]
// CHECK:STDOUT: %.loc11_18: type = ptr_type Inner [template = constants.%.1]
// CHECK:STDOUT: %.loc11_11: <unbound element of class Inner> = field_decl qi, element2 [template]
// CHECK:STDOUT: %.loc17_18: type = ptr_type Inner [template = constants.%.1]
// CHECK:STDOUT: %.loc17_11: <unbound element of class Inner> = field_decl qi, element2 [template]
// CHECK:STDOUT: %G: <function> = fn_decl @G [template] {}
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Inner
// CHECK:STDOUT: .pi = %.loc9_11
// CHECK:STDOUT: .po = %.loc10_11
// CHECK:STDOUT: .qi = %.loc11_11
// CHECK:STDOUT: .pi = %.loc15_11
// CHECK:STDOUT: .po = %.loc16_11
// CHECK:STDOUT: .qi = %.loc17_11
// CHECK:STDOUT: .G = %G
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F(%a: Outer*) {
// CHECK:STDOUT: fn @F.1() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [template = constants.%Outer]
// CHECK:STDOUT: %o.var: ref Outer = var o
// CHECK:STDOUT: %o: ref Outer = bind_name o, %o.var
// CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [template = constants.%Inner]
// CHECK:STDOUT: %.loc20_21: type = ptr_type Inner [template = constants.%.1]
// CHECK:STDOUT: %a.ref.loc20: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc20_26: ref Outer = deref %a.ref.loc20
// CHECK:STDOUT: %pi.ref.loc20: <unbound element of class Outer> = name_ref pi, @Outer.%.loc16_9 [template = @Outer.%.loc16_9]
// CHECK:STDOUT: %.loc20_29.1: ref Inner* = class_element_access %.loc20_26, element2
// CHECK:STDOUT: %.loc20_29.2: Inner* = bind_value %.loc20_29.1
// CHECK:STDOUT: %b: Inner* = bind_name b, %.loc20_29.2
// CHECK:STDOUT: %a.ref.loc22_5: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc22_4: ref Outer = deref %a.ref.loc22_5
// CHECK:STDOUT: %po.ref.loc22: <unbound element of class Outer> = name_ref po, @Outer.%.loc14_9 [template = @Outer.%.loc14_9]
// CHECK:STDOUT: %.loc22_7: ref Outer* = class_element_access %.loc22_4, element0
// CHECK:STDOUT: %a.ref.loc22_13: Outer* = name_ref a, %a
// CHECK:STDOUT: assign %.loc22_7, %a.ref.loc22_13
// CHECK:STDOUT: %a.ref.loc23_5: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc23_4: ref Outer = deref %a.ref.loc23_5
// CHECK:STDOUT: %qo.ref: <unbound element of class Outer> = name_ref qo, @Outer.%.loc15_9 [template = @Outer.%.loc15_9]
// CHECK:STDOUT: %.loc23_7: ref Outer* = class_element_access %.loc23_4, element1
// CHECK:STDOUT: %a.ref.loc23_13: Outer* = name_ref a, %a
// CHECK:STDOUT: assign %.loc23_7, %a.ref.loc23_13
// CHECK:STDOUT: %a.ref.loc24_5: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc24_4: ref Outer = deref %a.ref.loc24_5
// CHECK:STDOUT: %pi.ref.loc24_7: <unbound element of class Outer> = name_ref pi, @Outer.%.loc16_9 [template = @Outer.%.loc16_9]
// CHECK:STDOUT: %.loc24_7: ref Inner* = class_element_access %.loc24_4, element2
// CHECK:STDOUT: %a.ref.loc24_15: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc24_14: ref Outer = deref %a.ref.loc24_15
// CHECK:STDOUT: %pi.ref.loc24_17: <unbound element of class Outer> = name_ref pi, @Outer.%.loc16_9 [template = @Outer.%.loc16_9]
// CHECK:STDOUT: %.loc24_17.1: ref Inner* = class_element_access %.loc24_14, element2
// CHECK:STDOUT: %.loc24_17.2: Inner* = bind_value %.loc24_17.1
// CHECK:STDOUT: assign %.loc24_7, %.loc24_17.2
// CHECK:STDOUT: %b.ref.loc25: Inner* = name_ref b, %b
// CHECK:STDOUT: %.loc25_4: ref Inner = deref %b.ref.loc25
// CHECK:STDOUT: %po.ref.loc25: <unbound element of class Inner> = name_ref po, @Inner.%.loc10_11 [template = @Inner.%.loc10_11]
// CHECK:STDOUT: %.loc25_7: ref Outer* = class_element_access %.loc25_4, element1
// CHECK:STDOUT: %a.ref.loc25: Outer* = name_ref a, %a
// CHECK:STDOUT: assign %.loc25_7, %a.ref.loc25
// CHECK:STDOUT: %b.ref.loc26: Inner* = name_ref b, %b
// CHECK:STDOUT: %.loc26_4: ref Inner = deref %b.ref.loc26
// CHECK:STDOUT: %pi.ref.loc26_7: <unbound element of class Inner> = name_ref pi, @Inner.%.loc9_11 [template = @Inner.%.loc9_11]
// CHECK:STDOUT: %.loc26_7: ref Inner* = class_element_access %.loc26_4, element0
// CHECK:STDOUT: %a.ref.loc26: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc26_14: ref Outer = deref %a.ref.loc26
// CHECK:STDOUT: %pi.ref.loc26_17: <unbound element of class Outer> = name_ref pi, @Outer.%.loc16_9 [template = @Outer.%.loc16_9]
// CHECK:STDOUT: %.loc26_17.1: ref Inner* = class_element_access %.loc26_14, element2
// CHECK:STDOUT: %.loc26_17.2: Inner* = bind_value %.loc26_17.1
// CHECK:STDOUT: assign %.loc26_7, %.loc26_17.2
// CHECK:STDOUT: %b.ref.loc27: Inner* = name_ref b, %b
// CHECK:STDOUT: %.loc27_4: ref Inner = deref %b.ref.loc27
// CHECK:STDOUT: %qi.ref: <unbound element of class Inner> = name_ref qi, @Inner.%.loc11_11 [template = @Inner.%.loc11_11]
// CHECK:STDOUT: %.loc27_7: ref Inner* = class_element_access %.loc27_4, element2
// CHECK:STDOUT: %a.ref.loc27: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc27_14: ref Outer = deref %a.ref.loc27
// CHECK:STDOUT: %pi.ref.loc27: <unbound element of class Outer> = name_ref pi, @Outer.%.loc16_9 [template = @Outer.%.loc16_9]
// CHECK:STDOUT: %.loc27_17.1: ref Inner* = class_element_access %.loc27_14, element2
// CHECK:STDOUT: %.loc27_17.2: Inner* = bind_value %.loc27_17.1
// CHECK:STDOUT: assign %.loc27_7, %.loc27_17.2
// CHECK:STDOUT: %i.var: ref Inner = var i
// CHECK:STDOUT: %i: ref Inner = bind_name i, %i.var
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [template = constants.%Outer]
// CHECK:STDOUT: %o.var: ref Outer = var o
// CHECK:STDOUT: %o: ref Outer = bind_name o, %o.var
// CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [template = constants.%Inner]
// CHECK:STDOUT: %i.var: ref Inner = var i
// CHECK:STDOUT: %i: ref Inner = bind_name i, %i.var
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @H() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [template = constants.%Outer]
// CHECK:STDOUT: %o.var: ref Outer = var o
// CHECK:STDOUT: %o: ref Outer = bind_name o, %o.var
// CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [template = constants.%Inner]
// CHECK:STDOUT: %i.var: ref Inner = var i
// CHECK:STDOUT: %i: ref Inner = bind_name i, %i.var
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.2(%a: Outer*) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Outer.ref: type = name_ref Outer, file.%Outer.decl [template = constants.%Outer]
// CHECK:STDOUT: %Inner.ref: type = name_ref Inner, @Outer.%Inner.decl [template = constants.%Inner]
// CHECK:STDOUT: %.loc38_21: type = ptr_type Inner [template = constants.%.1]
// CHECK:STDOUT: %a.ref.loc38: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc38_26: ref Outer = deref %a.ref.loc38
// CHECK:STDOUT: %pi.ref.loc38: <unbound element of class Outer> = name_ref pi, @Outer.%.loc34_9 [template = @Outer.%.loc34_9]
// CHECK:STDOUT: %.loc38_29.1: ref Inner* = class_element_access %.loc38_26, element2
// CHECK:STDOUT: %.loc38_29.2: Inner* = bind_value %.loc38_29.1
// CHECK:STDOUT: %b: Inner* = bind_name b, %.loc38_29.2
// CHECK:STDOUT: %a.ref.loc40_3: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc40_4.1: ref Outer = deref %a.ref.loc40_3
// CHECK:STDOUT: %po.ref.loc40: <unbound element of class Outer> = name_ref po, @Outer.%.loc32_9 [template = @Outer.%.loc32_9]
// CHECK:STDOUT: %.loc40_4.2: ref Outer* = class_element_access %.loc40_4.1, element0
// CHECK:STDOUT: %a.ref.loc40_11: Outer* = name_ref a, %a
// CHECK:STDOUT: assign %.loc40_4.2, %a.ref.loc40_11
// CHECK:STDOUT: %a.ref.loc41_3: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc41_4.1: ref Outer = deref %a.ref.loc41_3
// CHECK:STDOUT: %qo.ref: <unbound element of class Outer> = name_ref qo, @Outer.%.loc33_9 [template = @Outer.%.loc33_9]
// CHECK:STDOUT: %.loc41_4.2: ref Outer* = class_element_access %.loc41_4.1, element1
// CHECK:STDOUT: %a.ref.loc41_11: Outer* = name_ref a, %a
// CHECK:STDOUT: assign %.loc41_4.2, %a.ref.loc41_11
// CHECK:STDOUT: %a.ref.loc42_3: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc42_4.1: ref Outer = deref %a.ref.loc42_3
// CHECK:STDOUT: %pi.ref.loc42_4: <unbound element of class Outer> = name_ref pi, @Outer.%.loc34_9 [template = @Outer.%.loc34_9]
// CHECK:STDOUT: %.loc42_4.2: ref Inner* = class_element_access %.loc42_4.1, element2
// CHECK:STDOUT: %a.ref.loc42_11: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc42_12.1: ref Outer = deref %a.ref.loc42_11
// CHECK:STDOUT: %pi.ref.loc42_12: <unbound element of class Outer> = name_ref pi, @Outer.%.loc34_9 [template = @Outer.%.loc34_9]
// CHECK:STDOUT: %.loc42_12.2: ref Inner* = class_element_access %.loc42_12.1, element2
// CHECK:STDOUT: %.loc42_12.3: Inner* = bind_value %.loc42_12.2
// CHECK:STDOUT: assign %.loc42_4.2, %.loc42_12.3
// CHECK:STDOUT: %b.ref.loc43: Inner* = name_ref b, %b
// CHECK:STDOUT: %.loc43_4.1: ref Inner = deref %b.ref.loc43
// CHECK:STDOUT: %po.ref.loc43: <unbound element of class Inner> = name_ref po, @Inner.%.loc16_11 [template = @Inner.%.loc16_11]
// CHECK:STDOUT: %.loc43_4.2: ref Outer* = class_element_access %.loc43_4.1, element1
// CHECK:STDOUT: %a.ref.loc43: Outer* = name_ref a, %a
// CHECK:STDOUT: assign %.loc43_4.2, %a.ref.loc43
// CHECK:STDOUT: %b.ref.loc44: Inner* = name_ref b, %b
// CHECK:STDOUT: %.loc44_4.1: ref Inner = deref %b.ref.loc44
// CHECK:STDOUT: %pi.ref.loc44_4: <unbound element of class Inner> = name_ref pi, @Inner.%.loc15_11 [template = @Inner.%.loc15_11]
// CHECK:STDOUT: %.loc44_4.2: ref Inner* = class_element_access %.loc44_4.1, element0
// CHECK:STDOUT: %a.ref.loc44: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc44_12.1: ref Outer = deref %a.ref.loc44
// CHECK:STDOUT: %pi.ref.loc44_12: <unbound element of class Outer> = name_ref pi, @Outer.%.loc34_9 [template = @Outer.%.loc34_9]
// CHECK:STDOUT: %.loc44_12.2: ref Inner* = class_element_access %.loc44_12.1, element2
// CHECK:STDOUT: %.loc44_12.3: Inner* = bind_value %.loc44_12.2
// CHECK:STDOUT: assign %.loc44_4.2, %.loc44_12.3
// CHECK:STDOUT: %b.ref.loc45: Inner* = name_ref b, %b
// CHECK:STDOUT: %.loc45_4.1: ref Inner = deref %b.ref.loc45
// CHECK:STDOUT: %qi.ref: <unbound element of class Inner> = name_ref qi, @Inner.%.loc17_11 [template = @Inner.%.loc17_11]
// CHECK:STDOUT: %.loc45_4.2: ref Inner* = class_element_access %.loc45_4.1, element2
// CHECK:STDOUT: %a.ref.loc45: Outer* = name_ref a, %a
// CHECK:STDOUT: %.loc45_12.1: ref Outer = deref %a.ref.loc45
// CHECK:STDOUT: %pi.ref.loc45: <unbound element of class Outer> = name_ref pi, @Outer.%.loc34_9 [template = @Outer.%.loc34_9]
// CHECK:STDOUT: %.loc45_12.2: ref Inner* = class_element_access %.loc45_12.1, element2
// CHECK:STDOUT: %.loc45_12.3: Inner* = bind_value %.loc45_12.2
// CHECK:STDOUT: assign %.loc45_4.2, %.loc45_12.3
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
+6 -6
View File
@@ -23,12 +23,12 @@ fn MemberNamedSelf.F(x: Self, y: r#Self) {}
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %Class: type = class_type @Class [template]
// CHECK:STDOUT: %.1: type = ptr_type Class [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %MemberNamedSelf: type = class_type @MemberNamedSelf [template]
// CHECK:STDOUT: %Self: type = class_type @Self [template]
// CHECK:STDOUT: %.1: type = struct_type {} [template]
// CHECK:STDOUT: %.2: type = ptr_type Class [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %MemberNamedSelf: type = class_type @MemberNamedSelf [template]
// CHECK:STDOUT: %Self: type = class_type @Self [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -81,11 +81,11 @@ fn MemberNamedSelf.F(x: Self, y: r#Self) {}
// CHECK:STDOUT: fn @F.1() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Self.ref.loc9: type = name_ref Self, constants.%Class [template = constants.%Class]
// CHECK:STDOUT: %.loc9: type = ptr_type Class [template = constants.%.1]
// CHECK:STDOUT: %.loc9: type = ptr_type Class [template = constants.%.2]
// CHECK:STDOUT: %Self.var: ref Class* = var r#Self
// CHECK:STDOUT: %Self: ref Class* = bind_name r#Self, %Self.var
// CHECK:STDOUT: %Self.ref.loc10_12: type = name_ref Self, constants.%Class [template = constants.%Class]
// CHECK:STDOUT: %.loc10_16: type = ptr_type Class [template = constants.%.1]
// CHECK:STDOUT: %.loc10_16: type = ptr_type Class [template = constants.%.2]
// CHECK:STDOUT: %p.var: ref Class* = var p
// CHECK:STDOUT: %p: ref Class* = bind_name p, %p.var
// CHECK:STDOUT: %Self.ref.loc10_20: ref Class* = name_ref r#Self, %Self
@@ -6,18 +6,6 @@
class Class {
fn G() -> i32 {
// TODO: This should find the member function `F` even though it's declared
// later.
// CHECK:STDERR: fail_reorder.carbon:[[@LINE+10]]:12: ERROR: Member access into incomplete class `Class`.
// CHECK:STDERR: return Class.F();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_reorder.carbon:[[@LINE-7]]:1: Class is incomplete within its definition.
// CHECK:STDERR: class Class {
// CHECK:STDERR: ^~~~~~~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_reorder.carbon:[[@LINE+3]]:12: ERROR: Name `F` not found.
// CHECK:STDERR: return Class.F();
// CHECK:STDERR: ^~~~~~~
return Class.F();
}
@@ -26,12 +14,12 @@ class Class {
}
}
// CHECK:STDOUT: --- fail_reorder.carbon
// CHECK:STDOUT: --- reorder.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %Class: type = class_type @Class [template]
// CHECK:STDOUT: %.1: i32 = int_literal 1 [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.1: type = struct_type {} [template]
// CHECK:STDOUT: %.2: i32 = int_literal 1 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -46,7 +34,7 @@ class Class {
// CHECK:STDOUT: %return.var.loc8: ref i32 = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %F: <function> = fn_decl @F [template] {
// CHECK:STDOUT: %return.var.loc24: ref i32 = var <return slot>
// CHECK:STDOUT: %return.var.loc12: ref i32 = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
@@ -58,13 +46,16 @@ class Class {
// CHECK:STDOUT: fn @G() -> i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [template = constants.%Class]
// CHECK:STDOUT: %F.ref: <error> = name_ref F, <error> [template = <error>]
// CHECK:STDOUT: return <error>
// CHECK:STDOUT: %F.ref: <function> = name_ref F, @Class.%F [template = @Class.%F]
// CHECK:STDOUT: %.loc9_19.1: init i32 = call %F.ref()
// CHECK:STDOUT: %.loc9_21: i32 = value_of_initializer %.loc9_19.1
// CHECK:STDOUT: %.loc9_19.2: i32 = converted %.loc9_19.1, %.loc9_21
// CHECK:STDOUT: return %.loc9_19.2
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() -> i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc25: i32 = int_literal 1 [template = constants.%.1]
// CHECK:STDOUT: return %.loc25
// CHECK:STDOUT: %.loc13: i32 = int_literal 1 [template = constants.%.2]
// CHECK:STDOUT: return %.loc13
// CHECK:STDOUT: }
// CHECK:STDOUT:
+192
View File
@@ -0,0 +1,192 @@
// 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
class A {
class B {
class C;
fn BF();
var b: i32;
}
class B.C {
class D {
fn F();
fn DF();
var d: i32;
}
fn D.DF() {
// A, B, C, and D are complete here.
var a: A = {.a = 1};
var b: B = {.b = 2};
var c: C = {.c = 3};
var d: D = {.d = 4};
// Unqualified lookup looks in all of them.
AF();
BF();
CF();
DF();
}
fn CF();
var c: i32;
}
fn AF();
var a: i32;
}
// CHECK:STDOUT: --- reorder_qualified.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %A: type = class_type @A [template]
// CHECK:STDOUT: %B: type = class_type @B [template]
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: %.1: type = unbound_element_type B, i32 [template]
// CHECK:STDOUT: %.2: type = struct_type {.b: i32} [template]
// CHECK:STDOUT: %D: type = class_type @D [template]
// CHECK:STDOUT: %.3: type = unbound_element_type D, i32 [template]
// CHECK:STDOUT: %.4: type = struct_type {.d: i32} [template]
// CHECK:STDOUT: %.5: type = unbound_element_type C, i32 [template]
// CHECK:STDOUT: %.6: type = struct_type {.c: i32} [template]
// CHECK:STDOUT: %.7: type = unbound_element_type A, i32 [template]
// CHECK:STDOUT: %.8: type = struct_type {.a: i32} [template]
// CHECK:STDOUT: %.9: type = ptr_type {.a: i32} [template]
// CHECK:STDOUT: %.10: i32 = int_literal 1 [template]
// CHECK:STDOUT: %.11: A = struct_value (%.10) [template]
// CHECK:STDOUT: %.12: type = ptr_type {.b: i32} [template]
// CHECK:STDOUT: %.13: i32 = int_literal 2 [template]
// CHECK:STDOUT: %.14: B = struct_value (%.13) [template]
// CHECK:STDOUT: %.15: type = ptr_type {.c: i32} [template]
// CHECK:STDOUT: %.16: i32 = int_literal 3 [template]
// CHECK:STDOUT: %.17: C = struct_value (%.16) [template]
// CHECK:STDOUT: %.18: type = ptr_type {.d: i32} [template]
// CHECK:STDOUT: %.19: i32 = int_literal 4 [template]
// CHECK:STDOUT: %.20: D = struct_value (%.19) [template]
// CHECK:STDOUT: %.21: type = tuple_type () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .A = %A.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %A.decl: type = class_decl @A [template = constants.%A] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @A {
// CHECK:STDOUT: %B.decl: type = class_decl @B [template = constants.%B] {}
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {}
// CHECK:STDOUT: %AF: <function> = fn_decl @AF [template] {}
// CHECK:STDOUT: %.loc42: <unbound element of class A> = field_decl a, element0 [template]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%A
// CHECK:STDOUT: .B = %B.decl
// CHECK:STDOUT: .AF = %AF
// CHECK:STDOUT: .a = %.loc42
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @B {
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {}
// CHECK:STDOUT: %BF: <function> = fn_decl @BF [template] {}
// CHECK:STDOUT: %.loc12: <unbound element of class B> = field_decl b, element0 [template]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%B
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: .BF = %BF
// CHECK:STDOUT: .b = %.loc12
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: %D.decl: type = class_decl @D [template = constants.%D] {}
// CHECK:STDOUT: %DF: <function> = fn_decl @DF [template] {}
// CHECK:STDOUT: %CF: <function> = fn_decl @CF [template] {}
// CHECK:STDOUT: %.loc38: <unbound element of class C> = field_decl c, element0 [template]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: .D = %D.decl
// CHECK:STDOUT: .CF = %CF
// CHECK:STDOUT: .c = %.loc38
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @D {
// CHECK:STDOUT: %F: <function> = fn_decl @F [template] {}
// CHECK:STDOUT: %DF: <function> = fn_decl @DF [template] {}
// CHECK:STDOUT: %.loc20: <unbound element of class D> = field_decl d, element0 [template]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%D
// CHECK:STDOUT: .F = %F
// CHECK:STDOUT: .DF = %DF
// CHECK:STDOUT: .d = %.loc20
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @BF();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @DF() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [template = constants.%A]
// CHECK:STDOUT: %a.var: ref A = var a
// CHECK:STDOUT: %a: ref A = bind_name a, %a.var
// CHECK:STDOUT: %.loc25_24: i32 = int_literal 1 [template = constants.%.10]
// CHECK:STDOUT: %.loc25_25.1: {.a: i32} = struct_literal (%.loc25_24)
// CHECK:STDOUT: %.loc25_25.2: ref i32 = class_element_access %a.var, element0
// CHECK:STDOUT: %.loc25_25.3: init i32 = initialize_from %.loc25_24 to %.loc25_25.2 [template = constants.%.10]
// CHECK:STDOUT: %.loc25_25.4: init A = class_init (%.loc25_25.3), %a.var [template = constants.%.11]
// CHECK:STDOUT: %.loc25_25.5: init A = converted %.loc25_25.1, %.loc25_25.4 [template = constants.%.11]
// CHECK:STDOUT: assign %a.var, %.loc25_25.5
// CHECK:STDOUT: %B.ref: type = name_ref B, @A.%B.decl [template = constants.%B]
// CHECK:STDOUT: %b.var: ref B = var b
// CHECK:STDOUT: %b: ref B = bind_name b, %b.var
// CHECK:STDOUT: %.loc26_24: i32 = int_literal 2 [template = constants.%.13]
// CHECK:STDOUT: %.loc26_25.1: {.b: i32} = struct_literal (%.loc26_24)
// CHECK:STDOUT: %.loc26_25.2: ref i32 = class_element_access %b.var, element0
// CHECK:STDOUT: %.loc26_25.3: init i32 = initialize_from %.loc26_24 to %.loc26_25.2 [template = constants.%.13]
// CHECK:STDOUT: %.loc26_25.4: init B = class_init (%.loc26_25.3), %b.var [template = constants.%.14]
// CHECK:STDOUT: %.loc26_25.5: init B = converted %.loc26_25.1, %.loc26_25.4 [template = constants.%.14]
// CHECK:STDOUT: assign %b.var, %.loc26_25.5
// CHECK:STDOUT: %C.ref: type = name_ref C, @B.%C.decl [template = constants.%C]
// CHECK:STDOUT: %c.var: ref C = var c
// CHECK:STDOUT: %c: ref C = bind_name c, %c.var
// CHECK:STDOUT: %.loc27_24: i32 = int_literal 3 [template = constants.%.16]
// CHECK:STDOUT: %.loc27_25.1: {.c: i32} = struct_literal (%.loc27_24)
// CHECK:STDOUT: %.loc27_25.2: ref i32 = class_element_access %c.var, element0
// CHECK:STDOUT: %.loc27_25.3: init i32 = initialize_from %.loc27_24 to %.loc27_25.2 [template = constants.%.16]
// CHECK:STDOUT: %.loc27_25.4: init C = class_init (%.loc27_25.3), %c.var [template = constants.%.17]
// CHECK:STDOUT: %.loc27_25.5: init C = converted %.loc27_25.1, %.loc27_25.4 [template = constants.%.17]
// CHECK:STDOUT: assign %c.var, %.loc27_25.5
// CHECK:STDOUT: %D.ref: type = name_ref D, @C.%D.decl [template = constants.%D]
// CHECK:STDOUT: %d.var: ref D = var d
// CHECK:STDOUT: %d: ref D = bind_name d, %d.var
// CHECK:STDOUT: %.loc28_24: i32 = int_literal 4 [template = constants.%.19]
// CHECK:STDOUT: %.loc28_25.1: {.d: i32} = struct_literal (%.loc28_24)
// CHECK:STDOUT: %.loc28_25.2: ref i32 = class_element_access %d.var, element0
// CHECK:STDOUT: %.loc28_25.3: init i32 = initialize_from %.loc28_24 to %.loc28_25.2 [template = constants.%.19]
// CHECK:STDOUT: %.loc28_25.4: init D = class_init (%.loc28_25.3), %d.var [template = constants.%.20]
// CHECK:STDOUT: %.loc28_25.5: init D = converted %.loc28_25.1, %.loc28_25.4 [template = constants.%.20]
// CHECK:STDOUT: assign %d.var, %.loc28_25.5
// CHECK:STDOUT: %AF.ref: <function> = name_ref AF, @A.%AF [template = @A.%AF]
// CHECK:STDOUT: %.loc31: init () = call %AF.ref()
// CHECK:STDOUT: %BF.ref: <function> = name_ref BF, @B.%BF [template = @B.%BF]
// CHECK:STDOUT: %.loc32: init () = call %BF.ref()
// CHECK:STDOUT: %CF.ref: <function> = name_ref CF, @C.%CF [template = @C.%CF]
// CHECK:STDOUT: %.loc33: init () = call %CF.ref()
// CHECK:STDOUT: %DF.ref: <function> = name_ref DF, @D.%DF [template = @D.%DF]
// CHECK:STDOUT: %.loc34: init () = call %DF.ref()
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @CF();
// CHECK:STDOUT:
// CHECK:STDOUT: fn @AF();
// CHECK:STDOUT:
+3 -3
View File
@@ -27,8 +27,8 @@ fn Run() {
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %Class: type = class_type @Class [template]
// CHECK:STDOUT: %.1: i32 = int_literal 1 [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: %.1: type = struct_type {} [template]
// CHECK:STDOUT: %.2: i32 = int_literal 1 [template]
// CHECK:STDOUT: %.3: i32 = int_literal 2 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -61,7 +61,7 @@ fn Run() {
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.1() -> i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc9: i32 = int_literal 1 [template = constants.%.1]
// CHECK:STDOUT: %.loc9: i32 = int_literal 1 [template = constants.%.2]
// CHECK:STDOUT: return %.loc9
// CHECK:STDOUT: }
// CHECK:STDOUT:
+14 -1
View File
@@ -10,7 +10,10 @@ interface Simple {
class C {
impl as Simple {
fn F() {}
fn F() {
// C is a complete type here.
var c: C = {};
}
}
}
@@ -23,6 +26,9 @@ class C {
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: %.4: <witness> = interface_witness (@impl.%F) [template]
// CHECK:STDOUT: %.5: type = struct_type {} [template]
// CHECK:STDOUT: %.6: type = tuple_type () [template]
// CHECK:STDOUT: %.7: type = ptr_type {} [template]
// CHECK:STDOUT: %.8: C = struct_value () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -67,6 +73,13 @@ class C {
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.2() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %c.var: ref C = var c
// CHECK:STDOUT: %c: ref C = bind_name c, %c.var
// CHECK:STDOUT: %.loc15_19.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc15_19.2: init C = class_init (), %c.var [template = constants.%.8]
// CHECK:STDOUT: %.loc15_19.3: init C = converted %.loc15_19.1, %.loc15_19.2 [template = constants.%.8]
// CHECK:STDOUT: assign %c.var, %.loc15_19.3
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
+5 -5
View File
@@ -23,9 +23,9 @@ class A {
// CHECK:STDOUT: %.2: type = assoc_entity_type @DefaultConstructible, <function> [template]
// CHECK:STDOUT: %.3: <associated <function> in DefaultConstructible> = assoc_entity element0, @DefaultConstructible.%Make [template]
// CHECK:STDOUT: %A: type = class_type @A [template]
// CHECK:STDOUT: %.4: i32 = int_literal 0 [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.%Make) [template]
// CHECK:STDOUT: %.6: type = struct_type {} [template]
// CHECK:STDOUT: %.4: <witness> = interface_witness (@impl.%Make) [template]
// CHECK:STDOUT: %.5: type = struct_type {} [template]
// CHECK:STDOUT: %.6: i32 = int_literal 0 [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -58,7 +58,7 @@ class A {
// CHECK:STDOUT: %Self.ref: type = name_ref Self, i32 [template = i32]
// CHECK:STDOUT: %return.var: ref i32 = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Make) [template = constants.%.5]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Make) [template = constants.%.4]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Make = %Make
@@ -78,7 +78,7 @@ class A {
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Make.2() -> i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc15: i32 = int_literal 0 [template = constants.%.4]
// CHECK:STDOUT: %.loc15: i32 = int_literal 0 [template = constants.%.6]
// CHECK:STDOUT: return %.loc15
// CHECK:STDOUT: }
// CHECK:STDOUT:
+10 -10
View File
@@ -43,10 +43,10 @@ impl D as SelfNested {
// CHECK:STDOUT: %D: type = class_type @D [template]
// CHECK:STDOUT: %.5: type = tuple_type () [template]
// CHECK:STDOUT: %.6: type = ptr_type {} [template]
// CHECK:STDOUT: %.7: C = struct_value () [template]
// CHECK:STDOUT: %.8: <witness> = interface_witness (@impl.1.%F) [template]
// CHECK:STDOUT: %.9: D = struct_value () [template]
// CHECK:STDOUT: %.10: <witness> = interface_witness (@impl.2.%F) [template]
// CHECK:STDOUT: %.7: <witness> = interface_witness (@impl.1.%F) [template]
// CHECK:STDOUT: %.8: C = struct_value () [template]
// CHECK:STDOUT: %.9: <witness> = interface_witness (@impl.2.%F) [template]
// CHECK:STDOUT: %.10: D = struct_value () [template]
// CHECK:STDOUT: %.11: type = interface_type @SelfNested [template]
// CHECK:STDOUT: %.12: type = ptr_type Self [symbolic]
// CHECK:STDOUT: %.13: type = struct_type {.x: Self, .y: i32} [symbolic]
@@ -154,7 +154,7 @@ impl D as SelfNested {
// CHECK:STDOUT: %C.ref.loc16_26: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F) [template = constants.%.8]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F) [template = constants.%.7]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %F
@@ -172,7 +172,7 @@ impl D as SelfNested {
// CHECK:STDOUT: %Self.ref.loc20_32: type = name_ref Self, constants.%D [template = constants.%D]
// CHECK:STDOUT: %return.var: ref D = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F) [template = constants.%.10]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F) [template = constants.%.9]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %F
@@ -230,16 +230,16 @@ impl D as SelfNested {
// CHECK:STDOUT: fn @F.2[@impl.1.%self.loc16_8.2: C](@impl.1.%x.loc16_17.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc16_38.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc16_38.2: init C = class_init (), @impl.1.%return.var [template = constants.%.7]
// CHECK:STDOUT: %.loc16_38.3: init C = converted %.loc16_38.1, %.loc16_38.2 [template = constants.%.7]
// CHECK:STDOUT: %.loc16_38.2: init C = class_init (), @impl.1.%return.var [template = constants.%.8]
// CHECK:STDOUT: %.loc16_38.3: init C = converted %.loc16_38.1, %.loc16_38.2 [template = constants.%.8]
// CHECK:STDOUT: return %.loc16_38.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.3[@impl.2.%self.loc20_8.2: D](@impl.2.%x.loc20_20.2: D) -> @impl.2.%return.var: D {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc20_47.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc20_47.2: init D = class_init (), @impl.2.%return.var [template = constants.%.9]
// CHECK:STDOUT: %.loc20_47.3: init D = converted %.loc20_47.1, %.loc20_47.2 [template = constants.%.9]
// CHECK:STDOUT: %.loc20_47.2: init D = class_init (), @impl.2.%return.var [template = constants.%.10]
// CHECK:STDOUT: %.loc20_47.3: init D = converted %.loc20_47.1, %.loc20_47.2 [template = constants.%.10]
// CHECK:STDOUT: return %.loc20_47.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
+10 -14
View File
@@ -4,10 +4,6 @@
//
// AUTOUPDATE
// TODO: The errors here are in bad locations. We should build a SemIR
// representation for a reference to a name so that we can track the location
// properly.
namespace N;
// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+4]]:14: ERROR: Expression cannot be used as a value.
// CHECK:STDERR: var a: i32 = N[0];
@@ -70,20 +66,20 @@ var d: i32 = {.a: i32, .b: i32}[0];
// CHECK:STDOUT: fn @__global_init() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %N.ref: <namespace> = name_ref N, file.%N [template = file.%N]
// CHECK:STDOUT: %.loc16: i32 = int_literal 0 [template = constants.%.1]
// CHECK:STDOUT: %.loc12: i32 = int_literal 0 [template = constants.%.1]
// CHECK:STDOUT: assign file.%a.var, <error>
// CHECK:STDOUT: %F.ref: <function> = name_ref F, file.%F [template = file.%F]
// CHECK:STDOUT: %.loc23: i32 = int_literal 1 [template = constants.%.2]
// CHECK:STDOUT: %.loc19: i32 = int_literal 1 [template = constants.%.2]
// CHECK:STDOUT: assign file.%b.var, <error>
// CHECK:STDOUT: %.loc29_20: i32 = int_literal 1 [template = constants.%.2]
// CHECK:STDOUT: %.loc29_28: i32 = int_literal 2 [template = constants.%.3]
// CHECK:STDOUT: %.loc29_29.1: {.a: i32, .b: i32} = struct_literal (%.loc29_20, %.loc29_28)
// CHECK:STDOUT: %.loc29_31: i32 = int_literal 0 [template = constants.%.1]
// CHECK:STDOUT: %.loc29_29.2: {.a: i32, .b: i32} = struct_value (%.loc29_20, %.loc29_28) [template = constants.%.6]
// CHECK:STDOUT: %.loc29_29.3: {.a: i32, .b: i32} = converted %.loc29_29.1, %.loc29_29.2 [template = constants.%.6]
// CHECK:STDOUT: %.loc25_20: i32 = int_literal 1 [template = constants.%.2]
// CHECK:STDOUT: %.loc25_28: i32 = int_literal 2 [template = constants.%.3]
// CHECK:STDOUT: %.loc25_29.1: {.a: i32, .b: i32} = struct_literal (%.loc25_20, %.loc25_28)
// CHECK:STDOUT: %.loc25_31: i32 = int_literal 0 [template = constants.%.1]
// CHECK:STDOUT: %.loc25_29.2: {.a: i32, .b: i32} = struct_value (%.loc25_20, %.loc25_28) [template = constants.%.6]
// CHECK:STDOUT: %.loc25_29.3: {.a: i32, .b: i32} = converted %.loc25_29.1, %.loc25_29.2 [template = constants.%.6]
// CHECK:STDOUT: assign file.%c.var, <error>
// CHECK:STDOUT: %.loc34_31: type = struct_type {.a: i32, .b: i32} [template = constants.%.4]
// CHECK:STDOUT: %.loc34_33: i32 = int_literal 0 [template = constants.%.1]
// CHECK:STDOUT: %.loc30_31: type = struct_type {.a: i32, .b: i32} [template = constants.%.4]
// CHECK:STDOUT: %.loc30_33: i32 = int_literal 0 [template = constants.%.1]
// CHECK:STDOUT: assign file.%d.var, <error>
// CHECK:STDOUT: return
// CHECK:STDOUT: }
+96
View File
@@ -0,0 +1,96 @@
// 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
class C {
interface I {
// TODO: Use `default` here.
fn F() {
// I and F are both complete here, and the impl below is in scope.
var c: C = {};
c.(I.F)();
}
}
impl C as I {
fn F() {}
}
}
// CHECK:STDOUT: --- default_fn.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: %.1: type = interface_type @I [template]
// CHECK:STDOUT: %.2: type = assoc_entity_type @I, <function> [template]
// CHECK:STDOUT: %.3: <associated <function> in I> = assoc_entity element0, @I.%F [template]
// CHECK:STDOUT: %.4: <witness> = interface_witness (@impl.%F) [template]
// CHECK:STDOUT: %.5: type = struct_type {} [template]
// CHECK:STDOUT: %.6: type = tuple_type () [template]
// CHECK:STDOUT: %.7: type = ptr_type {} [template]
// CHECK:STDOUT: %.8: C = struct_value () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @I {
// CHECK:STDOUT: %Self: I = bind_symbolic_name Self [symbolic]
// CHECK:STDOUT: %F: <function> = fn_decl @F.1 [template] {}
// CHECK:STDOUT: %.loc10: <associated <function> in I> = assoc_entity element0, %F [template = constants.%.3]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: .F = %.loc10
// CHECK:STDOUT: witness = (%F)
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: C as I {
// CHECK:STDOUT: %F: <function> = fn_decl @F.2 [template] {}
// CHECK:STDOUT: %.1: <witness> = interface_witness (%F) [template = constants.%.4]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = %F
// CHECK:STDOUT: witness = %.1
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {}
// CHECK:STDOUT: impl_decl @impl {
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [template = constants.%.1]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: .I = %I.decl
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.1() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %c.var: ref C = var c
// CHECK:STDOUT: %c: ref C = bind_name c, %c.var
// CHECK:STDOUT: %.loc12_19.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc12_19.2: init C = class_init (), %c.var [template = constants.%.8]
// CHECK:STDOUT: %.loc12_19.3: init C = converted %.loc12_19.1, %.loc12_19.2 [template = constants.%.8]
// CHECK:STDOUT: assign %c.var, %.loc12_19.3
// CHECK:STDOUT: %c.ref: ref C = name_ref c, %c
// CHECK:STDOUT: %I.ref: type = name_ref I, @C.%I.decl [template = constants.%.1]
// CHECK:STDOUT: %F.ref: <associated <function> in I> = name_ref F, @I.%.loc10 [template = constants.%.3]
// CHECK:STDOUT: %.1: <function> = interface_witness_access @impl.%.1, element0 [template = @impl.%F]
// CHECK:STDOUT: %.loc13: init () = call %.1()
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F.2() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
+5 -5
View File
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @Add [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @AddAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @BitAnd [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @BitAndAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -78,8 +78,8 @@ fn TestOp(a: C) -> C {
// CHECK:STDOUT: %.2: type = interface_type @BitComplement [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = assoc_entity_type @BitComplement, <function> [template]
// CHECK:STDOUT: %.8: <associated <function> in BitComplement> = assoc_entity element0, file.%import_ref.6 [template]
// CHECK:STDOUT: }
@@ -128,7 +128,7 @@ fn TestOp(a: C) -> C {
// CHECK:STDOUT: %C.ref.loc9_23: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -143,8 +143,8 @@ fn TestOp(a: C) -> C {
// CHECK:STDOUT: fn @Op.1[@impl.%self.loc9_9.2: C]() -> @impl.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @BitOr [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @BitOrAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @BitXor [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @BitXorAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
+5 -5
View File
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @Div [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @DivAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @LeftShift [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @LeftShiftAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
+5 -5
View File
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @Mod [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @ModAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
+5 -5
View File
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @Mul [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @MulAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -78,8 +78,8 @@ fn TestOp(a: C) -> C {
// CHECK:STDOUT: %.2: type = interface_type @Negate [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = assoc_entity_type @Negate, <function> [template]
// CHECK:STDOUT: %.8: <associated <function> in Negate> = assoc_entity element0, file.%import_ref.6 [template]
// CHECK:STDOUT: }
@@ -128,7 +128,7 @@ fn TestOp(a: C) -> C {
// CHECK:STDOUT: %C.ref.loc9_23: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -143,8 +143,8 @@ fn TestOp(a: C) -> C {
// CHECK:STDOUT: fn @Op.1[@impl.%self.loc9_9.2: C]() -> @impl.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @RightShift [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @RightShiftAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
+5 -5
View File
@@ -125,8 +125,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %.2: type = interface_type @Sub [template]
// CHECK:STDOUT: %.3: type = tuple_type () [template]
// CHECK:STDOUT: %.4: type = ptr_type {} [template]
// CHECK:STDOUT: %.5: C = struct_value () [template]
// CHECK:STDOUT: %.6: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.5: <witness> = interface_witness (@impl.1.%Op) [template]
// CHECK:STDOUT: %.6: C = struct_value () [template]
// CHECK:STDOUT: %.7: type = interface_type @SubAssign [template]
// CHECK:STDOUT: %.8: type = ptr_type C [template]
// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic]
@@ -216,7 +216,7 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: %C.ref.loc9_31: type = name_ref C, file.%C.decl [template = constants.%C]
// CHECK:STDOUT: %return.var: ref C = var <return slot>
// CHECK:STDOUT: }
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.6]
// CHECK:STDOUT: %.1: <witness> = interface_witness (%Op) [template = constants.%.5]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Op = %Op
@@ -249,8 +249,8 @@ fn TestAssign(a: C*, b: C) {
// CHECK:STDOUT: fn @Op.1[@impl.1.%self.loc9_9.2: C](@impl.1.%other.loc9_18.2: C) -> @impl.1.%return.var: C {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %.loc10_13.1: {} = struct_literal ()
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.5]
// CHECK:STDOUT: %.loc10_13.2: init C = class_init (), @impl.1.%return.var [template = constants.%.6]
// CHECK:STDOUT: %.loc10_13.3: init C = converted %.loc10_13.1, %.loc10_13.2 [template = constants.%.6]
// CHECK:STDOUT: return %.loc10_13.3
// CHECK:STDOUT: }
// CHECK:STDOUT:
+1 -1
View File
@@ -18,7 +18,7 @@ fn Main() {
// --- inside_fn.carbon
package InsideFn api;
package Insidefinition api;
var x: i32 = 0;
+1
View File
@@ -186,6 +186,7 @@ cc_library(
":token_kind",
":tokenized_buffer",
"//common:check",
"//common:variant_helpers",
"//toolchain/base:value_store",
"//toolchain/diagnostics:diagnostic_emitter",
"//toolchain/source:source_buffer",
+1 -22
View File
@@ -7,6 +7,7 @@
#include <array>
#include "common/check.h"
#include "common/variant_helpers.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Compiler.h"
@@ -177,28 +178,6 @@ class [[clang::internal_linkage]] Lexer {
TokenDiagnosticEmitter token_emitter_;
};
// TODO: Move Overload and VariantMatch somewhere more central.
// Form an overload set from a list of functions. For example:
//
// ```
// auto overloaded = Overload{[] (int) {}, [] (float) {}};
// ```
template <typename... Fs>
struct Overload : Fs... {
using Fs::operator()...;
};
template <typename... Fs>
Overload(Fs...) -> Overload<Fs...>;
// Pattern-match against the type of the value stored in the variant `V`. Each
// element of `fs` should be a function that takes one or more of the variant
// values in `V`.
template <typename V, typename... Fs>
auto VariantMatch(V&& v, Fs&&... fs) -> decltype(auto) {
return std::visit(Overload{std::forward<Fs&&>(fs)...}, std::forward<V&&>(v));
}
#if CARBON_USE_SIMD
namespace {
#if __ARM_NEON
+44
View File
@@ -11,8 +11,11 @@
#include "llvm/ADT/STLExtras.h"
#include "toolchain/lex/token_kind.h"
#include "toolchain/lex/tokenized_buffer.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/parse/node_kind.h"
#include "toolchain/parse/state.h"
#include "toolchain/parse/tree.h"
#include "toolchain/parse/typed_nodes.h"
namespace Carbon::Parse {
@@ -424,6 +427,47 @@ auto Context::EmitExpectedDeclSemiOrDefinition(Lex::TokenKind expected_kind)
emitter().Emit(*position(), ExpectedDeclSemiOrDefinition, expected_kind);
}
// Returns whether we are currently parsing in a scope in which function
// definitions are deferred, such as a class or interface.
static auto ParsingInDeferredDefinitionScope(Context& context) -> bool {
auto& stack = context.state_stack();
if (stack.size() < 2 || stack.back().state != State::DeclScopeLoop) {
return false;
}
auto state = stack[stack.size() - 2].state;
return state == State::DeclDefinitionFinishAsClass ||
state == State::DeclDefinitionFinishAsImpl ||
state == State::DeclDefinitionFinishAsInterface ||
state == State::DeclDefinitionFinishAsNamedConstraint;
}
auto Context::AddFunctionDefinitionStart(Lex::TokenIndex token,
int subtree_start, bool has_error)
-> void {
if (ParsingInDeferredDefinitionScope(*this)) {
enclosing_deferred_definition_stack_.push_back(
tree_->deferred_definitions_.Add(
{.start_id = FunctionDefinitionStartId(
NodeId(tree_->node_impls_.size()))}));
}
AddNode(NodeKind::FunctionDefinitionStart, token, subtree_start, has_error);
}
auto Context::AddFunctionDefinition(Lex::TokenIndex token, int subtree_start,
bool has_error) -> void {
if (ParsingInDeferredDefinitionScope(*this)) {
auto definition_index = enclosing_deferred_definition_stack_.pop_back_val();
auto& definition = tree_->deferred_definitions_.Get(definition_index);
definition.definition_id =
FunctionDefinitionId(NodeId(tree_->node_impls_.size()));
definition.next_definition_index =
DeferredDefinitionIndex(tree_->deferred_definitions().size());
}
AddNode(NodeKind::FunctionDefinition, token, subtree_start, has_error);
}
auto Context::PrintForStackDump(llvm::raw_ostream& output) const -> void {
output << "Parser stack:\n";
for (auto [i, entry] : llvm::enumerate(state_stack_)) {
+14
View File
@@ -307,6 +307,15 @@ class Context {
tree_->imports_.push_back(package);
}
// Adds a function definition start node, and begins tracking a deferred
// definition if necessary.
auto AddFunctionDefinitionStart(Lex::TokenIndex token, int subtree_start,
bool has_error) -> void;
// Adds a function definition node, and ends tracking a deferred definition if
// necessary.
auto AddFunctionDefinition(Lex::TokenIndex token, int subtree_start,
bool has_error) -> void;
// Prints information for a stack dump.
auto PrintForStackDump(llvm::raw_ostream& output) const -> void;
@@ -358,6 +367,11 @@ class Context {
llvm::SmallVector<StateStackEntry> state_stack_;
// The deferred definition indexes of functions whose definitions have begun
// but not yet finished.
llvm::SmallVector<DeferredDefinitionIndex>
enclosing_deferred_definition_stack_;
// The current packaging state, whether `import`/`package` are allowed.
PackagingState packaging_state_ = PackagingState::FileStart;
// The first non-packaging token, starting as invalid. Used for packaging
+4 -4
View File
@@ -44,8 +44,8 @@ auto HandleFunctionSignatureFinish(Context& context) -> void {
break;
}
case Lex::TokenKind::OpenCurlyBrace: {
context.AddNode(NodeKind::FunctionDefinitionStart, context.Consume(),
state.subtree_start, state.has_error);
context.AddFunctionDefinitionStart(context.Consume(), state.subtree_start,
state.has_error);
// Any error is recorded on the FunctionDefinitionStart.
state.has_error = false;
context.PushState(state, State::FunctionDefinitionFinish);
@@ -93,8 +93,8 @@ auto HandleFunctionSignatureFinish(Context& context) -> void {
auto HandleFunctionDefinitionFinish(Context& context) -> void {
auto state = context.PopState();
context.AddNode(NodeKind::FunctionDefinition, context.Consume(),
state.subtree_start, state.has_error);
context.AddFunctionDefinition(context.Consume(), state.subtree_start,
state.has_error);
}
} // namespace Carbon::Parse
+37
View File
@@ -16,9 +16,41 @@
#include "toolchain/lex/tokenized_buffer.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/parse/node_kind.h"
#include "toolchain/parse/typed_nodes.h"
namespace Carbon::Parse {
struct DeferredDefinition;
// The index of a deferred function definition within the parse tree's deferred
// definition store.
struct DeferredDefinitionIndex : public IndexBase {
using ValueType = DeferredDefinition;
static const DeferredDefinitionIndex Invalid;
using IndexBase::IndexBase;
};
constexpr DeferredDefinitionIndex DeferredDefinitionIndex::Invalid =
DeferredDefinitionIndex(InvalidIndex);
// A function whose definition is deferred because it is defined inline in a
// class or similar scope.
//
// Such functions are type-checked out of order, with their bodies checked after
// the enclosing declaration is complete. Some additional information is tracked
// for these functions in the parse tree to support this reordering.
struct DeferredDefinition {
// The node that starts the function definition.
FunctionDefinitionStartId start_id;
// The function definition node.
FunctionDefinitionId definition_id = NodeId::Invalid;
// The index of the next method that is not nested within this one.
DeferredDefinitionIndex next_definition_index =
DeferredDefinitionIndex::Invalid;
};
// Defined in typed_nodes.h. Include that to call `Tree::ExtractFile()`.
struct File;
@@ -149,6 +181,10 @@ class Tree : public Printable<Tree> {
return packaging_directive_;
}
auto imports() const -> llvm::ArrayRef<PackagingNames> { return imports_; }
auto deferred_definitions() const
-> const ValueStore<DeferredDefinitionIndex>& {
return deferred_definitions_;
}
// See the other Print comments.
auto Print(llvm::raw_ostream& output) const -> void;
@@ -339,6 +375,7 @@ class Tree : public Printable<Tree> {
std::optional<PackagingDirective> packaging_directive_;
llvm::SmallVector<PackagingNames> imports_;
ValueStore<DeferredDefinitionIndex> deferred_definitions_;
};
// A random-access iterator to the depth-first postorder sequence of parse nodes