Redeclaration support for impl declarations. (#3717)

Maintain a mapping from (self type, constraint) to `ImplId` on the
`Impl` value store so that we can perform redeclaration lookup.
This commit is contained in:
Richard Smith
2024-02-22 20:26:36 +00:00
committed by GitHub
parent b1850920e4
commit eed33c3b8c
8 changed files with 157 additions and 58 deletions
+1 -1
View File
@@ -365,7 +365,7 @@ class Context {
auto interfaces() -> ValueStore<SemIR::InterfaceId>& {
return sem_ir().interfaces();
}
auto impls() -> ValueStore<SemIR::ImplId>& { return sem_ir().impls(); }
auto impls() -> SemIR::ImplStore& { return sem_ir().impls(); }
auto import_irs() -> ValueStore<SemIR::ImportIRId>& {
return sem_ir().import_irs();
}
+8 -12
View File
@@ -153,21 +153,17 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId parse_node)
auto name_context = context.decl_name_stack().FinishImplName();
CARBON_CHECK(name_context.state == DeclNameStack::NameContext::State::Empty);
// Add the impl declaration.
auto impl_decl = SemIR::ImplDecl{SemIR::ImplId::Invalid, decl_block_id};
auto impl_decl_id = context.AddPlaceholderInst({parse_node, impl_decl});
// TODO: Check for an orphan `impl`.
// TODO: Check whether this is a redeclaration.
// TODO: Check parameters. Store them on the `Impl` in some form.
static_cast<void>(params_id);
// Create a new impl if this isn't a valid redeclaration.
if (!impl_decl.impl_id.is_valid()) {
impl_decl.impl_id = context.impls().Add(
{.self_id = self_type_id, .constraint_id = constraint_type_id});
}
// Write the impl ID into the ImplDecl.
context.ReplaceInstBeforeConstantUse(impl_decl_id, {parse_node, impl_decl});
// Add the impl declaration.
// TODO: Does lookup in an impl file need to look for a prior impl declaration
// in the api file?
auto impl_id = context.impls().LookupOrAdd(self_type_id, constraint_type_id);
auto impl_decl = SemIR::ImplDecl{impl_id, decl_block_id};
auto impl_decl_id = context.AddInst({parse_node, impl_decl});
// For an `extend impl` declaration, mark the impl as extending this `impl`.
if (!!(context.decl_state_stack().innermost().modifier_set &
+41
View File
@@ -0,0 +1,41 @@
// 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
interface I {}
impl i32 as I {}
// CHECK:STDERR: fail_redefinition.carbon:[[@LINE+6]]:1: ERROR: Redefinition of `impl i32 as I`.
// CHECK:STDERR: impl i32 as I {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~
// CHECK:STDERR: fail_redefinition.carbon:[[@LINE-5]]:1: Previous definition was here.
// CHECK:STDERR: impl i32 as I {}
// CHECK:STDERR: ^~~~~~~~~~~~~~~
impl i32 as I {}
// CHECK:STDOUT: --- fail_redefinition.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: type = interface_type @I [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {.I = %I.decl} [template]
// CHECK:STDOUT: %I.decl = interface_decl @I, ()
// CHECK:STDOUT: impl_decl @impl, (<unexpected instref inst+3>)
// CHECK:STDOUT: impl_decl @impl, (<unexpected instref inst+5>)
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @I {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl: i32 as I {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -6,22 +6,28 @@
interface I {}
impl i32 as I;
class X {
impl i32 as I;
}
impl i32 as I {}
// TODO: Reject this redefinition.
impl i32 as I {}
// CHECK:STDOUT: --- todo_redefinition.carbon
// CHECK:STDOUT: --- redeclaration.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %.1: type = interface_type @I [template]
// CHECK:STDOUT: %X: type = class_type @X [template]
// CHECK:STDOUT: %.2: type = struct_type {} [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace {.I = %I.decl} [template]
// CHECK:STDOUT: package: <namespace> = namespace {.I = %I.decl, .X = %X.decl} [template]
// CHECK:STDOUT: %I.decl = interface_decl @I, ()
// CHECK:STDOUT: impl_decl @impl.1, (<unexpected instref inst+3>)
// CHECK:STDOUT: impl_decl @impl.2, (<unexpected instref inst+5>)
// CHECK:STDOUT: impl_decl @impl, (<unexpected instref inst+3>)
// CHECK:STDOUT: %X.decl = class_decl @X, ()
// CHECK:STDOUT: impl_decl @impl, (<unexpected instref inst+10>)
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @I {
@@ -29,12 +35,13 @@ impl i32 as I {}
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl.1: i32 as I {
// CHECK:STDOUT: impl @impl: i32 as I {
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: impl @impl.2: i32 as I {
// CHECK:STDOUT: class @X {
// CHECK:STDOUT: impl_decl @impl, (<unexpected instref inst+7>)
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: }
+1
View File
@@ -69,6 +69,7 @@ cc_library(
],
hdrs = [
"file.h",
"impl.h",
"value_stores.h",
],
deps = [
+4 -36
View File
@@ -13,6 +13,7 @@
#include "toolchain/base/value_store.h"
#include "toolchain/base/yaml.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/impl.h"
#include "toolchain/sem_ir/type_info.h"
#include "toolchain/sem_ir/value_stores.h"
@@ -175,39 +176,6 @@ struct Interface : public Printable<Interface> {
bool defined = false;
};
// An implementation of a constraint.
struct Impl : public Printable<Impl> {
auto Print(llvm::raw_ostream& out) const -> void {
out << "{self: " << self_id << ", constraint: " << constraint_id << "}";
}
// Determines whether this impl has been fully defined. This is false until we
// reach the `}` of the impl definition.
auto is_defined() const -> bool { return defined; }
// The following members always have values, and do not change throughout the
// lifetime of the interface.
// TODO: Track the generic parameters for `impl forall`.
// The type for which the impl is implementing a constraint.
TypeId self_id;
// The constraint that the impl implements.
TypeId constraint_id;
// The following members are set at the `{` of the impl definition.
// The definition of the impl. This is an ImplDecl.
InstId definition_id = InstId::Invalid;
// The impl scope.
NameScopeId scope_id = NameScopeId::Invalid;
// The first block of the impl body.
// TODO: Handle control flow in the impl body, such as if-expressions.
InstBlockId body_block_id = InstBlockId::Invalid;
// The following members are set at the `}` of the impl definition.
bool defined = false;
};
// Provides semantic analysis on a Parse::Tree.
class File : public Printable<File> {
public:
@@ -302,8 +270,8 @@ class File : public Printable<File> {
auto interfaces() const -> const ValueStore<InterfaceId>& {
return interfaces_;
}
auto impls() -> ValueStore<ImplId>& { return impls_; }
auto impls() const -> const ValueStore<ImplId>& { return impls_; }
auto impls() -> ImplStore& { return impls_; }
auto impls() const -> const ImplStore& { return impls_; }
auto import_irs() -> ValueStore<ImportIRId>& { return import_irs_; }
auto import_irs() const -> const ValueStore<ImportIRId>& {
return import_irs_;
@@ -378,7 +346,7 @@ class File : public Printable<File> {
ValueStore<InterfaceId> interfaces_;
// Storage for impls.
ValueStore<ImplId> impls_;
ImplStore impls_;
// Related IRs. There will always be at least one entry, the builtin IR (used
// for references of builtins).
+3
View File
@@ -487,5 +487,8 @@ struct llvm::DenseMapInfo<Carbon::SemIR::InstId>
template <>
struct llvm::DenseMapInfo<Carbon::SemIR::NameScopeId>
: public Carbon::IndexMapInfo<Carbon::SemIR::NameScopeId> {};
template <>
struct llvm::DenseMapInfo<Carbon::SemIR::TypeId>
: public Carbon::IndexMapInfo<Carbon::SemIR::TypeId> {};
#endif // CARBON_TOOLCHAIN_SEM_IR_IDS_H_
+83
View File
@@ -0,0 +1,83 @@
// 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_SEM_IR_IMPL_H_
#define CARBON_TOOLCHAIN_SEM_IR_IMPL_H_
#include "llvm/ADT/DenseMap.h"
#include "toolchain/sem_ir/ids.h"
namespace Carbon::SemIR {
// An implementation of a constraint.
struct Impl : public Printable<Impl> {
auto Print(llvm::raw_ostream& out) const -> void {
out << "{self: " << self_id << ", constraint: " << constraint_id << "}";
}
// Determines whether this impl has been fully defined. This is false until we
// reach the `}` of the impl definition.
auto is_defined() const -> bool { return defined; }
// The following members always have values, and do not change throughout the
// lifetime of the interface.
// TODO: Track the generic parameters for `impl forall`.
// The type for which the impl is implementing a constraint.
TypeId self_id;
// The constraint that the impl implements.
TypeId constraint_id;
// The following members are set at the `{` of the impl definition.
// The definition of the impl. This is an ImplDecl.
InstId definition_id = InstId::Invalid;
// The impl scope.
NameScopeId scope_id = NameScopeId::Invalid;
// The first block of the impl body.
// TODO: Handle control flow in the impl body, such as if-expressions.
InstBlockId body_block_id = InstBlockId::Invalid;
// The following members are set at the `}` of the impl definition.
bool defined = false;
};
// A collection of `Impl`s, which can be accessed by the self type and
// constraint implemented.
class ImplStore {
public:
// Looks up the impl with this self type and constraint, or creates a new
// `Impl` if none exists.
// TODO: Handle parameters.
auto LookupOrAdd(TypeId self_id, TypeId constraint_id) -> ImplId {
auto [it, added] =
lookup_.insert({{self_id, constraint_id}, ImplId::Invalid});
if (added) {
it->second =
values_.Add({.self_id = self_id, .constraint_id = constraint_id});
}
return it->second;
}
// Returns a mutable value for an ID.
auto Get(ImplId id) -> Impl& { return values_.Get(id); }
// Returns the value for an ID.
auto Get(ImplId id) const -> const Impl& { return values_.Get(id); }
auto OutputYaml() const -> Yaml::OutputMapping {
return values_.OutputYaml();
}
auto array_ref() const -> llvm::ArrayRef<Impl> { return values_.array_ref(); }
auto size() const -> size_t { return values_.size(); }
private:
ValueStore<ImplId> values_;
llvm::DenseMap<std::pair<TypeId, TypeId>, ImplId> lookup_;
};
} // namespace Carbon::SemIR
#endif // CARBON_TOOLCHAIN_SEM_IR_IMPL_H_