From b74d3f80f17e209cff877511b2cb92d436f67e92 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 10 Jun 2022 14:16:54 -0700 Subject: [PATCH] Add a new kind of Witness value that carries an expression (#1324) This renames `Witness` to `ImplWitness` and adds a new form, `SymbolicWitness`, that holds an expression by which a witness can be computed. A common base class `Witness` is provided. We form the new kind of witness when evaluation of a witness expression fails because the witness is not in scope, as happens when evaluating a subexpression such as a type expression in isolation, and retry evaluation in the larger context when the interpreter performs type instantiation when running the code. This allows us to properly handle compile-time evaluation of constructs involving witness table lookups when the witness can be statically determined. The intent is that we will eventually also form symbolic witness table references when impl selection finds a non-final witness, in order to support specialization. Simplify `NominalClassValue`: it can now always store a witness map rather than either a witness map or a witness-or-witness-expression map. --- explorer/interpreter/action.cpp | 3 + explorer/interpreter/action.h | 18 +++ explorer/interpreter/action_stack.cpp | 2 + explorer/interpreter/action_stack.h | 5 + explorer/interpreter/interpreter.cpp | 143 +++++++++--------- explorer/interpreter/type_checker.cpp | 22 +-- explorer/interpreter/value.cpp | 45 +++--- explorer/interpreter/value.h | 75 ++++----- .../generic_class/use_at_compile_time.carbon | 28 ++++ .../call_at_compile_time.carbon | 25 +++ 10 files changed, 228 insertions(+), 138 deletions(-) create mode 100644 explorer/testdata/generic_class/use_at_compile_time.carbon create mode 100644 explorer/testdata/generic_function/call_at_compile_time.carbon diff --git a/explorer/interpreter/action.cpp b/explorer/interpreter/action.cpp index 5ad6534b2da8..e2adbda74c8d 100644 --- a/explorer/interpreter/action.cpp +++ b/explorer/interpreter/action.cpp @@ -116,6 +116,9 @@ void Action::Print(llvm::raw_ostream& out) const { break; case Action::Kind::ScopeAction: break; + case Action::Kind::RecursiveAction: + out << "recursive"; + break; } out << "." << pos_ << "."; if (!results_.empty()) { diff --git a/explorer/interpreter/action.h b/explorer/interpreter/action.h index 88e6bb81d249..46ce4115a529 100644 --- a/explorer/interpreter/action.h +++ b/explorer/interpreter/action.h @@ -85,6 +85,7 @@ class Action { StatementAction, DeclarationAction, ScopeAction, + RecursiveAction, }; Action(const Value&) = delete; @@ -250,6 +251,23 @@ class ScopeAction : public Action { } }; +// Action which contains another action and does nothing further once that +// action completes. This action therefore acts as a marker on the action stack +// that indicates that the interpreter should stop when the inner action has +// finished, and holds the result of that inner action. This is useful to allow +// a sequence of steps for an action to be run immediately rather than as part +// of the normal step queue. +// +// Should be avoided where possible. +class RecursiveAction : public Action { + public: + explicit RecursiveAction() : Action(Kind::RecursiveAction) {} + + static auto classof(const Action* action) -> bool { + return action->kind() == Kind::RecursiveAction; + } +}; + } // namespace Carbon #endif // CARBON_EXPLORER_INTERPRETER_ACTION_H_ diff --git a/explorer/interpreter/action_stack.cpp b/explorer/interpreter/action_stack.cpp index 1535e5a3ac77..4a51dfeac902 100644 --- a/explorer/interpreter/action_stack.cpp +++ b/explorer/interpreter/action_stack.cpp @@ -124,6 +124,7 @@ auto ActionStack::FinishAction() -> ErrorOr { CARBON_FATAL() << "ScopeAction at top of stack"; case Action::Kind::StatementAction: case Action::Kind::DeclarationAction: + case Action::Kind::RecursiveAction: PopScopes(); } return Success(); @@ -135,6 +136,7 @@ auto ActionStack::FinishAction(Nonnull result) switch (act->kind()) { case Action::Kind::StatementAction: case Action::Kind::DeclarationAction: + case Action::Kind::RecursiveAction: CARBON_FATAL() << "This kind of Action cannot produce results: " << *act; case Action::Kind::ScopeAction: CARBON_FATAL() << "ScopeAction at top of stack"; diff --git a/explorer/interpreter/action_stack.h b/explorer/interpreter/action_stack.h index 49e5711b67f2..877434cc8404 100644 --- a/explorer/interpreter/action_stack.h +++ b/explorer/interpreter/action_stack.h @@ -88,6 +88,11 @@ class ActionStack { auto Spawn(std::unique_ptr child, RuntimeScope scope) -> ErrorOr; + // Start a new recursive action. + auto BeginRecursiveAction() { + todo_.Push(std::make_unique()); + } + // Advances the current action one step. auto RunAgain() -> ErrorOr; diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 08fb63bc29fd..baa918991c36 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -91,17 +91,13 @@ class Interpreter { // Returns the result of converting `value` to type `destination_type`. auto Convert(Nonnull value, Nonnull destination_type, - SourceLocation source_loc) const - -> ErrorOr>; + SourceLocation source_loc) -> ErrorOr>; - // Evaluate an impl expression to produce a witness, or signal an - // error. + // Evaluate an expression immediately, recursively. // - // An impl expression is either - // 1) an IdentifierExpression whose value_node is an impl declaration, or - // 2) an InstantiateImpl expression. - auto EvalImplExp(Nonnull exp) const - -> ErrorOr>; + // TODO: Stop using this. + auto EvalExpRecursively(Nonnull exp) + -> ErrorOr>; // Instantiate a type by replacing all type variables that occur inside the // type by the current values of those variables. @@ -110,8 +106,7 @@ class Interpreter { // __Fn (Point(T)) -> Point(U) // becomes // __Fn (Point(i32)) -> Point(Bool) - auto InstantiateType(Nonnull type, - SourceLocation source_loc) const + auto InstantiateType(Nonnull type, SourceLocation source_loc) -> ErrorOr>; // Call the function `fun` with the given `arg` and the `witnesses` @@ -421,39 +416,34 @@ auto Interpreter::StepLvalue() -> ErrorOr { } } -auto Interpreter::EvalImplExp(Nonnull exp) const - -> ErrorOr> { - switch (exp->kind()) { - case ExpressionKind::InstantiateImpl: { - const InstantiateImpl& inst_impl = cast(*exp); - CARBON_ASSIGN_OR_RETURN(Nonnull gen_impl, - EvalImplExp(inst_impl.generic_impl())); - ImplWitnessMap witnesses; - for (auto& [bind, impl_exp] : inst_impl.impls()) { - CARBON_ASSIGN_OR_RETURN(witnesses[bind], EvalImplExp(impl_exp)); - } - return arena_->New(&gen_impl->declaration(), - inst_impl.type_args(), witnesses); - } - case ExpressionKind::IdentifierExpression: { - const auto& ident = cast(*exp); - 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())); - } - return cast(value); - } - default: { - CARBON_FATAL() << "EvalImplExp, unexpected expression: " << *exp; +auto Interpreter::EvalExpRecursively(Nonnull exp) + -> ErrorOr> { + if (trace_stream_) { + **trace_stream_ << "--- recursive eval of " << *exp << "\n"; + PrintState(**trace_stream_); + } + todo_.BeginRecursiveAction(); + CARBON_RETURN_IF_ERROR(todo_.Spawn(std::make_unique(exp))); + // Note that the only `RecursiveAction` we can encounter here is our own -- + // if a nested action begins a recursive action, it will run until that + // action is finished and popped off the queue before returning to us. + while (!isa(todo_.CurrentAction())) { + CARBON_RETURN_IF_ERROR(Step()); + if (trace_stream_) { + PrintState(**trace_stream_); } } + if (trace_stream_) { + **trace_stream_ << "--- recursive eval done\n"; + } + Nonnull result = + cast(todo_.CurrentAction()).results()[0]; + CARBON_RETURN_IF_ERROR(todo_.FinishAction()); + return result; } auto Interpreter::InstantiateType(Nonnull type, - SourceLocation source_loc) const + SourceLocation source_loc) -> ErrorOr> { switch (type->kind()) { case Value::Kind::VariableType: { @@ -473,9 +463,12 @@ auto Interpreter::InstantiateType(Nonnull type, CARBON_ASSIGN_OR_RETURN(inst_type_args[ty_var], InstantiateType(ty_arg, source_loc)); } - ImplWitnessMap witnesses; - for (const auto& [bind, impl_exp] : class_type.impls()) { - CARBON_ASSIGN_OR_RETURN(witnesses[bind], EvalImplExp(impl_exp)); + ImplWitnessMap witnesses = class_type.witnesses(); + for (auto& [bind, witness] : witnesses) { + if (auto* sym = dyn_cast(witness)) { + CARBON_ASSIGN_OR_RETURN(witness, + EvalExpRecursively(&sym->impl_expression())); + } } return arena_->New(&class_type.declaration(), inst_type_args, witnesses); @@ -487,7 +480,7 @@ auto Interpreter::InstantiateType(Nonnull type, auto Interpreter::Convert(Nonnull value, Nonnull destination_type, - SourceLocation source_loc) const + SourceLocation source_loc) -> ErrorOr> { switch (value->kind()) { case Value::Kind::IntValue: @@ -507,7 +500,8 @@ auto Interpreter::Convert(Nonnull value, case Value::Kind::NominalClassType: case Value::Kind::InterfaceType: case Value::Kind::ConstraintType: - case Value::Kind::Witness: + case Value::Kind::ImplWitness: + case Value::Kind::SymbolicWitness: case Value::Kind::ParameterizedEntityName: case Value::Kind::ChoiceType: case Value::Kind::ContinuationType: @@ -696,27 +690,12 @@ auto Interpreter::CallFunction(const CallExpression& call, ¶ms_scope, generic_args, trace_stream_, this->arena_)); switch (decl.kind()) { - case DeclarationKind::ClassDeclaration: { - switch (phase()) { - case Phase::RunTime: - return todo_.FinishAction(arena_->New( - &cast(decl), generic_args, witnesses)); - case Phase::CompileTime: - return todo_.FinishAction(arena_->New( - &cast(decl), generic_args, call.impls())); - } - } - case DeclarationKind::InterfaceDeclaration: { - switch (phase()) { - case Phase::RunTime: - return todo_.FinishAction(arena_->New( - &cast(decl), generic_args, witnesses)); - case Phase::CompileTime: - return todo_.FinishAction( - arena_->New(&cast(decl), - generic_args, call.impls())); - } - } + case DeclarationKind::ClassDeclaration: + return todo_.FinishAction(arena_->New( + &cast(decl), generic_args, witnesses)); + case DeclarationKind::InterfaceDeclaration: + return todo_.FinishAction(arena_->New( + &cast(decl), generic_args, witnesses)); default: CARBON_FATAL() << "unknown kind of ParameterizedEntityName " << decl; } @@ -740,20 +719,24 @@ auto Interpreter::StepExp() -> ErrorOr { if (act.pos() == 0) { return todo_.Spawn( std::make_unique(inst_impl.generic_impl())); - } else if (act.pos() - 1 < int(inst_impl.impls().size())) { + } + if (act.pos() == 1 && isa(act.results()[0])) { + return todo_.FinishAction(arena_->New(&exp)); + } + if (act.pos() - 1 < int(inst_impl.impls().size())) { auto iter = inst_impl.impls().begin(); std::advance(iter, act.pos() - 1); return todo_.Spawn(std::make_unique(iter->second)); } else { - Nonnull generic_witness = - cast(act.results()[0]); + Nonnull generic_witness = + cast(act.results()[0]); ImplWitnessMap witnesses; int i = 0; for (const auto& [impl_bind, impl_exp] : inst_impl.impls()) { witnesses[impl_bind] = cast(act.results()[i + 1]); ++i; } - return todo_.FinishAction(arena_->New( + return todo_.FinishAction(arena_->New( &generic_witness->declaration(), inst_impl.type_args(), witnesses)); } } @@ -764,6 +747,9 @@ auto Interpreter::StepExp() -> ErrorOr { return todo_.Spawn(std::make_unique( &cast(exp).object())); } else if (act.pos() == 1) { + if (isa(act.results()[0])) { + return todo_.FinishAction(arena_->New(&exp)); + } return todo_.Spawn(std::make_unique( &cast(exp).offset())); } else { @@ -924,9 +910,16 @@ 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} - CARBON_ASSIGN_OR_RETURN( - Nonnull value, - todo_.ValueOfNode(ident.value_node(), ident.source_loc())); + 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)); if (const auto* lvalue = dyn_cast(value)) { CARBON_ASSIGN_OR_RETURN( value, heap_.Read(lvalue->address(), exp.source_loc())); @@ -965,9 +958,7 @@ auto Interpreter::StepExp() -> ErrorOr { } case ExpressionKind::CallExpression: { const CallExpression& call = cast(exp); - // Don't evaluate the impls at compile time? - unsigned int num_impls = - phase() == Phase::CompileTime ? 0 : call.impls().size(); + unsigned int num_impls = call.impls().size(); if (act.pos() == 0) { // { {e1(e2) :: C, E, F} :: S, H} // -> { {e1 :: [](e2) :: C, E, F} :: S, H} @@ -1468,6 +1459,8 @@ auto Interpreter::Step() -> ErrorOr { break; case Action::Kind::ScopeAction: CARBON_FATAL() << "ScopeAction escaped ActionStack"; + case Action::Kind::RecursiveAction: + CARBON_FATAL() << "Tried to step a RecursiveAction"; } // switch return Success(); } diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 7c3a586dd9fe..08cf46106c1d 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -82,7 +82,8 @@ static auto IsTypeOfType(Nonnull value) -> bool { case Value::Kind::AlternativeConstructorValue: case Value::Kind::ContinuationValue: case Value::Kind::StringValue: - case Value::Kind::Witness: + case Value::Kind::ImplWitness: + case Value::Kind::SymbolicWitness: case Value::Kind::ParameterizedEntityName: case Value::Kind::MemberName: case Value::Kind::TypeOfParameterizedEntityName: @@ -137,7 +138,8 @@ static auto IsType(Nonnull value, bool concrete = false) -> bool { case Value::Kind::AlternativeConstructorValue: case Value::Kind::ContinuationValue: case Value::Kind::StringValue: - case Value::Kind::Witness: + case Value::Kind::ImplWitness: + case Value::Kind::SymbolicWitness: case Value::Kind::ParameterizedEntityName: case Value::Kind::MemberName: return false; @@ -704,7 +706,8 @@ auto TypeChecker::ArgumentDeduction( case Value::Kind::TypeOfParameterizedEntityName: case Value::Kind::TypeOfMemberName: return handle_non_deduced_type(); - case Value::Kind::Witness: + case Value::Kind::ImplWitness: + case Value::Kind::SymbolicWitness: case Value::Kind::ParameterizedEntityName: case Value::Kind::MemberName: case Value::Kind::IntValue: @@ -993,7 +996,8 @@ auto TypeChecker::Substitute( // TODO: We should substitute into the value and produce a new type of // type for it. return type; - case Value::Kind::Witness: + case Value::Kind::ImplWitness: + case Value::Kind::SymbolicWitness: case Value::Kind::ParameterizedEntityName: case Value::Kind::MemberName: case Value::Kind::IntValue: @@ -1904,8 +1908,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, case DeclarationKind::ClassDeclaration: { Nonnull inst_class_type = arena_->New(&cast(decl), - call.deduced_args(), - call.impls()); + call.deduced_args()); call.set_static_type( arena_->New(inst_class_type)); call.set_value_category(ValueCategory::Let); @@ -1914,7 +1917,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, case DeclarationKind::InterfaceDeclaration: { Nonnull inst_iface_type = arena_->New(&cast(decl), - call.deduced_args(), call.impls()); + call.deduced_args()); call.set_static_type( arena_->New(inst_iface_type)); call.set_value_category(ValueCategory::Let); @@ -2991,7 +2994,7 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull impl_decl, } } } - impl_decl->set_constant_value(arena_->New(impl_decl)); + impl_decl->set_constant_value(arena_->New(impl_decl)); if (trace_stream_) { **trace_stream_ << "** finished declaring impl " << *impl_decl->impl_type() << " as " << impl_decl->interface() << "\n"; @@ -3053,7 +3056,8 @@ static bool IsValidTypeForAliasTarget(Nonnull type) { case Value::Kind::NominalClassValue: case Value::Kind::AlternativeValue: case Value::Kind::TupleValue: - case Value::Kind::Witness: + case Value::Kind::ImplWitness: + case Value::Kind::SymbolicWitness: case Value::Kind::ParameterizedEntityName: case Value::Kind::MemberName: case Value::Kind::BindingPlaceholderValue: diff --git a/explorer/interpreter/value.cpp b/explorer/interpreter/value.cpp index bed3fe84d2e1..fd3c8b485479 100644 --- a/explorer/interpreter/value.cpp +++ b/explorer/interpreter/value.cpp @@ -36,28 +36,35 @@ static auto GetMember(Nonnull arena, Nonnull v, const std::string& f = field.name(); if (field.witness().has_value()) { - Nonnull witness = *field.witness(); + Nonnull witness = cast(*field.witness()); switch (witness->kind()) { - case Value::Kind::Witness: { + case Value::Kind::ImplWitness: { + auto* impl_witness = cast(witness); if (std::optional> mem_decl = - FindMember(f, witness->declaration().members()); + FindMember(f, impl_witness->declaration().members()); mem_decl.has_value()) { const auto& fun_decl = cast(**mem_decl); if (fun_decl.is_method()) { - return arena->New( - &fun_decl, v, witness->type_args(), witness->witnesses()); + return arena->New(&fun_decl, v, + impl_witness->type_args(), + impl_witness->witnesses()); } else { // Class function. - auto fun = cast(*fun_decl.constant_value()); + auto* fun = cast(*fun_decl.constant_value()); return arena->New(&fun->declaration(), - witness->type_args(), - witness->witnesses()); + impl_witness->type_args(), + impl_witness->witnesses()); } } else { return CompilationError(source_loc) << "member " << f << " not in " << *witness; } } + case Value::Kind::SymbolicWitness: { + return RuntimeError(source_loc) + << "member lookup for " << f << " in symbolic " << *witness + << " not implemented yet"; + } default: CARBON_FATAL() << "expected Witness, not " << *witness; } @@ -337,13 +344,6 @@ void Value::Print(llvm::raw_ostream& out) const { out << "class "; PrintNameWithBindings(out, &class_type.declaration(), class_type.type_args()); - if (!class_type.impls().empty()) { - out << " impls "; - llvm::ListSeparator sep; - for (const auto& [impl_bind, impl] : class_type.impls()) { - out << sep << *impl; - } - } if (!class_type.witnesses().empty()) { out << " witnesses "; llvm::ListSeparator sep; @@ -385,12 +385,17 @@ void Value::Print(llvm::raw_ostream& out) const { } break; } - case Value::Kind::Witness: { - const auto& witness = cast(*this); + case Value::Kind::ImplWitness: { + const auto& witness = cast(*this); out << "witness " << *witness.declaration().impl_type() << " as " << witness.declaration().interface(); break; } + case Value::Kind::SymbolicWitness: { + const auto& witness = cast(*this); + out << "witness " << witness.impl_expression(); + break; + } case Value::Kind::ParameterizedEntityName: out << *GetName(cast(*this).declaration()); break; @@ -660,7 +665,8 @@ auto TypeEqual(Nonnull t1, Nonnull t2) -> bool { CARBON_FATAL() << "TypeEqual used to compare non-type values\n" << *t1 << "\n" << *t2; - case Value::Kind::Witness: + case Value::Kind::ImplWitness: + case Value::Kind::SymbolicWitness: CARBON_FATAL() << "TypeEqual: unexpected Witness"; break; case Value::Kind::AutoType: @@ -748,7 +754,8 @@ auto ValueEqual(Nonnull v1, Nonnull v2) -> bool { case Value::Kind::NominalClassType: case Value::Kind::InterfaceType: case Value::Kind::ConstraintType: - case Value::Kind::Witness: + case Value::Kind::ImplWitness: + case Value::Kind::SymbolicWitness: case Value::Kind::ChoiceType: case Value::Kind::ContinuationType: case Value::Kind::VariableType: diff --git a/explorer/interpreter/value.h b/explorer/interpreter/value.h index 05a07d78dda3..6cad900e1109 100644 --- a/explorer/interpreter/value.h +++ b/explorer/interpreter/value.h @@ -45,7 +45,8 @@ class Value { NominalClassValue, AlternativeValue, TupleValue, - Witness, + ImplWitness, + SymbolicWitness, IntType, BoolType, TypeType, @@ -564,17 +565,6 @@ class NominalClassType : public Value { declaration_(declaration), type_args_(type_args) {} - // Construct a class type that represents the result of applying the - // given generic class to the `type_args` and that records the result of the - // compile-time search for any required impls. - explicit NominalClassType(Nonnull declaration, - const BindingMap& type_args, - const ImplExpMap& impls) - : Value(Kind::NominalClassType), - declaration_(declaration), - type_args_(type_args), - impls_(impls) {} - // Construct a fully instantiated generic class type to represent the // run-time type of an object. explicit NominalClassType(Nonnull declaration, @@ -592,13 +582,6 @@ class NominalClassType : public Value { auto declaration() const -> const ClassDeclaration& { return *declaration_; } auto type_args() const -> const BindingMap& { return type_args_; } - // Maps each of an instantiated generic class's impl bindings to an - // expression that constructs the witness table for the corresponding - // argument. Should not be called on 1) a non-generic class, 2) a - // generic-class that is not instantiated, or 3) a fully - // instantiated runtime type of a generic class. - auto impls() const -> const ImplExpMap& { return impls_; } - // Maps each of the class's impl bindings to the witness table // for the corresponding argument. Should only be called on a fully // instantiated runtime type of a generic class. @@ -618,7 +601,6 @@ class NominalClassType : public Value { private: Nonnull declaration_; BindingMap type_args_; - ImplExpMap impls_; ImplWitnessMap witnesses_; }; @@ -639,12 +621,6 @@ class InterfaceType : public Value { explicit InterfaceType(Nonnull declaration, const BindingMap& args) : Value(Kind::InterfaceType), declaration_(declaration), args_(args) {} - explicit InterfaceType(Nonnull declaration, - const BindingMap& args, const ImplExpMap& impls) - : Value(Kind::InterfaceType), - declaration_(declaration), - args_(args), - impls_(impls) {} explicit InterfaceType(Nonnull declaration, const BindingMap& args, const ImplWitnessMap& wits) : Value(Kind::InterfaceType), @@ -662,13 +638,11 @@ class InterfaceType : public Value { auto args() const -> const BindingMap& { return args_; } // TODO: These aren't used for anything yet. - auto impls() const -> const ImplExpMap& { return impls_; } auto witnesses() const -> const ImplWitnessMap& { return witnesses_; } private: Nonnull declaration_; BindingMap args_; - ImplExpMap impls_; ImplWitnessMap witnesses_; }; @@ -744,25 +718,37 @@ class ConstraintType : public Value { std::vector lookup_contexts_; }; -// The witness table for an impl. +// A witness table. class Witness : public Value { + protected: + explicit Witness(Value::Kind kind) : Value(kind) {} + + public: + static auto classof(const Value* value) -> bool { + return value->kind() == Kind::ImplWitness || + value->kind() == Kind::SymbolicWitness; + } +}; + +// The witness table for an impl. +class ImplWitness : public Witness { public: // Construct a witness for // 1) a non-generic impl, or // 2) a generic impl that has not yet been applied to type arguments. - explicit Witness(Nonnull declaration) - : Value(Kind::Witness), declaration_(declaration) {} + explicit ImplWitness(Nonnull declaration) + : Witness(Kind::ImplWitness), declaration_(declaration) {} // Construct an instantiated generic impl. - explicit Witness(Nonnull declaration, - const BindingMap& type_args, const ImplWitnessMap& wits) - : Value(Kind::Witness), + explicit ImplWitness(Nonnull declaration, + const BindingMap& type_args, const ImplWitnessMap& wits) + : Witness(Kind::ImplWitness), declaration_(declaration), type_args_(type_args), witnesses_(wits) {} static auto classof(const Value* value) -> bool { - return value->kind() == Kind::Witness; + return value->kind() == Kind::ImplWitness; } auto declaration() const -> const ImplDeclaration& { return *declaration_; } auto type_args() const -> const BindingMap& { return type_args_; } @@ -777,6 +763,25 @@ class Witness : public Value { ImplWitnessMap witnesses_; }; +// A witness table whose concrete value cannot be determined yet. +// +// These are used to represent symbolic witness values which can be computed at +// runtime but whose values are not known statically. +class SymbolicWitness : public Witness { + public: + explicit SymbolicWitness(Nonnull impl_expr) + : Witness(Kind::SymbolicWitness), impl_expr_(impl_expr) {} + + static auto classof(const Value* value) -> bool { + return value->kind() == Kind::SymbolicWitness; + } + + auto impl_expression() const -> const Expression& { return *impl_expr_; } + + private: + Nonnull impl_expr_; +}; + // A choice type. class ChoiceType : public Value { public: diff --git a/explorer/testdata/generic_class/use_at_compile_time.carbon b/explorer/testdata/generic_class/use_at_compile_time.carbon new file mode 100644 index 000000000000..6c268dc50daa --- /dev/null +++ b/explorer/testdata/generic_class/use_at_compile_time.carbon @@ -0,0 +1,28 @@ +// 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; + +interface Has(T:! Type) { + fn Get() -> T; +} +impl i32 as Has(Type) { + fn Get() -> Type { return Self; } +} + +class WithType(T:! Has(Type)) { + fn Get() -> Type { return T.Get(); } +} + +fn Main() -> i32 { + var v: WithType(i32).Get() = 0; + return v; +} diff --git a/explorer/testdata/generic_function/call_at_compile_time.carbon b/explorer/testdata/generic_function/call_at_compile_time.carbon new file mode 100644 index 000000000000..0ace774c63a9 --- /dev/null +++ b/explorer/testdata/generic_function/call_at_compile_time.carbon @@ -0,0 +1,25 @@ +// 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; + +interface X { fn F(); } +impl i32 as X { fn F() {} } + +fn G[T:! X](v: T) -> Type { + v.F(); + return i32; +} + +fn Main() -> i32 { + var v: G(0) = 0; + return v; +}