diff --git a/explorer/ast/impl_binding.h b/explorer/ast/impl_binding.h index 3204cccb829c..5031611d0e2d 100644 --- a/explorer/ast/impl_binding.h +++ b/explorer/ast/impl_binding.h @@ -53,7 +53,11 @@ class ImplBinding : public AstNode { return std::nullopt; } auto symbolic_identity() const -> std::optional> { - return std::nullopt; + return symbolic_identity_; + } + void set_symbolic_identity(Nonnull value) { + CARBON_CHECK(!symbolic_identity_.has_value()); + symbolic_identity_ = value; } // The static type of the impl. Cannot be called before typechecking. @@ -81,6 +85,7 @@ class ImplBinding : public AstNode { private: Nonnull type_var_; Nonnull iface_; + std::optional> symbolic_identity_; std::optional> static_type_; std::optional> original_; }; diff --git a/explorer/interpreter/action_stack.cpp b/explorer/interpreter/action_stack.cpp index 4a51dfeac902..c4597fff3a98 100644 --- a/explorer/interpreter/action_stack.cpp +++ b/explorer/interpreter/action_stack.cpp @@ -52,11 +52,9 @@ void ActionStack::Initialize(ValueNodeView value_node, auto ActionStack::ValueOfNode(ValueNodeView value_node, SourceLocation source_loc) const -> ErrorOr> { - std::optional value = (phase_ == Phase::CompileTime) - ? value_node.symbolic_identity() - : value_node.constant_value(); - if (value.has_value()) { - return *value; + std::optional constant_value = value_node.constant_value(); + if (constant_value.has_value()) { + return *constant_value; } for (const std::unique_ptr& action : todo_) { // TODO: have static name resolution identify the scope of value_node @@ -77,6 +75,20 @@ auto ActionStack::ValueOfNode(ValueNodeView value_node, return *result; } } + // We don't know the value of this node, but at compile time we may still be + // able to form a symbolic value for it. For example, in + // + // fn F[T:! Type](x: T) {} + // + // ... we don't know the value of `T` but can still symbolically evaluate it + // to a `VariableType`. At runtime we need actual values. + if (phase_ == Phase::CompileTime) { + std::optional symbolic_identity = + value_node.symbolic_identity(); + if (symbolic_identity.has_value()) { + return *symbolic_identity; + } + } // TODO: Move these errors to compile time and explain them more clearly. return RuntimeError(source_loc) << "could not find `" << value_node.base() << "`"; diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 2bf6a26acf80..46a6e2ab736b 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -918,16 +918,9 @@ auto Interpreter::StepExp() -> ErrorOr { CARBON_CHECK(act.pos() == 0); const auto& ident = cast(exp); // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H} - auto value_or_error = - todo_.ValueOfNode(ident.value_node(), ident.source_loc()); - if (!value_or_error.ok() && phase() == Phase::CompileTime && - isa(ident.value_node().base())) { - // The `ImplBinding` might not be in scope. If so, just remember the - // expression from which it was derived. - return todo_.FinishAction(arena_->New(&exp)); - } - CARBON_ASSIGN_OR_RETURN(Nonnull value, - std::move(value_or_error)); + CARBON_ASSIGN_OR_RETURN( + Nonnull value, + todo_.ValueOfNode(ident.value_node(), ident.source_loc())); if (const auto* lvalue = dyn_cast(value)) { CARBON_ASSIGN_OR_RETURN( value, heap_.Read(lvalue->address(), exp.source_loc())); diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 078fe2904700..931027e8ae7a 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -2348,6 +2348,8 @@ auto TypeChecker::TypeCheckPattern( if (isa(type)) { Nonnull impl_binding = arena_->New(binding.source_loc(), &binding, type); + impl_binding->set_symbolic_identity( + arena_->New(CreateImplReference(impl_binding))); binding.set_impl_binding(impl_binding); BringImplIntoScope(impl_binding, impl_scope); } diff --git a/explorer/testdata/generic_class/instantiation_at_compile_time.carbon b/explorer/testdata/generic_class/instantiation_at_compile_time.carbon new file mode 100644 index 000000000000..0df9059adefc --- /dev/null +++ b/explorer/testdata/generic_class/instantiation_at_compile_time.carbon @@ -0,0 +1,24 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// RUN: %{explorer} %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s +// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \ +// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: %{explorer} %s +// CHECK: result: 0 + +package ExplorerTest api; + +class C(T:! Type) {} + +fn F(T:! Type) -> Type { + return C(T); +} + +fn Main() -> i32 { + var v: F(i32) = {}; + var w: C(i32) = v; + return 0; +}