diff --git a/explorer/interpreter/BUILD b/explorer/interpreter/BUILD index f388f3f93092..bd3f6faa8ac5 100644 --- a/explorer/interpreter/BUILD +++ b/explorer/interpreter/BUILD @@ -117,7 +117,9 @@ cc_library( ":action", ":action_stack", ":heap", + ":pattern_match", ":stack", + ":type_utils", "//common:check", "//common:error", "//common:ostream", @@ -205,8 +207,10 @@ cc_library( ":dictionary", ":interpreter", ":pattern_analysis", + ":pattern_match", ":stack_space", ":type_structure", + ":type_utils", "//common:check", "//common:enum_base", "//common:error", @@ -268,3 +272,37 @@ cc_library( "@llvm-project//llvm:Support", ], ) + +cc_library( + name = "type_utils", + srcs = [ + "type_utils.cpp", + ], + hdrs = [ + "type_utils.h", + ], + deps = [ + "//explorer/ast", + "//explorer/common:nonnull", + "@llvm-project//llvm:Support", + ], +) + +cc_library( + name = "pattern_match", + srcs = [ + "pattern_match.cpp", + ], + hdrs = [ + "pattern_match.h", + ], + deps = [ + ":action", + "//explorer/ast", + "//explorer/common:arena", + "//explorer/common:nonnull", + "//explorer/common:source_location", + "//explorer/common:trace_stream", + "@llvm-project//llvm:Support", + ], +) diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 0bcdffb3cf96..2f8d2a4d3141 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -28,7 +28,9 @@ #include "explorer/common/trace_stream.h" #include "explorer/interpreter/action.h" #include "explorer/interpreter/action_stack.h" +#include "explorer/interpreter/pattern_match.h" #include "explorer/interpreter/stack.h" +#include "explorer/interpreter/type_utils.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" @@ -297,209 +299,6 @@ auto Interpreter::CreateStruct(const std::vector& fields, return arena_->New(std::move(elements)); } -static auto InitializePlaceholderValue(const ValueNodeView& value_node, - ExpressionResult v, - Nonnull bindings) { - switch (value_node.expression_category()) { - case ExpressionCategory::Reference: - if (v.expression_category() == ExpressionCategory::Value || - v.expression_category() == ExpressionCategory::Reference) { - // Build by copying from value or reference expression. - bindings->Initialize(value_node, v.value()); - } else { - // Location initialized by initializing expression, bind node to - // address. - CARBON_CHECK(v.address()) - << "Missing location from initializing expression"; - bindings->Bind(value_node, *v.address()); - } - break; - case ExpressionCategory::Value: - if (v.expression_category() == ExpressionCategory::Value) { - // We assume values are strictly nested for now. - bindings->BindValue(value_node, v.value()); - } else if (v.expression_category() == ExpressionCategory::Reference) { - // Bind the reference expression value directly. - CARBON_CHECK(v.address()) - << "Missing location from reference expression"; - bindings->BindAndPin(value_node, *v.address()); - } else { - // Location initialized by initializing expression, bind node to - // address. - CARBON_CHECK(v.address()) - << "Missing location from initializing expression"; - bindings->Bind(value_node, *v.address()); - } - break; - case ExpressionCategory::Initializing: - CARBON_FATAL() << "Cannot pattern match an initializing expression"; - break; - } -} - -auto PatternMatch(Nonnull p, ExpressionResult v, - SourceLocation source_loc, - std::optional> bindings, - BindingMap& generic_args, Nonnull trace_stream, - Nonnull arena) -> bool { - if (trace_stream->is_enabled()) { - *trace_stream << "match pattern " << *p << "\nfrom " - << ExpressionCategoryToString(v.expression_category()) - << " expression with value " << *v.value() << "\n"; - } - const auto make_expr_result = - [](Nonnull v) -> ExpressionResult { - if (const auto* expr_v = dyn_cast(v)) { - return ExpressionResult::Reference(expr_v->value(), expr_v->address()); - } - return ExpressionResult::Value(v); - }; - if (v.value()->kind() == Value::Kind::ReferenceExpressionValue) { - return PatternMatch(p, make_expr_result(v.value()), source_loc, bindings, - generic_args, trace_stream, arena); - } - switch (p->kind()) { - case Value::Kind::BindingPlaceholderValue: { - CARBON_CHECK(bindings.has_value()); - const auto& placeholder = cast(*p); - if (placeholder.value_node().has_value()) { - InitializePlaceholderValue(*placeholder.value_node(), v, *bindings); - } - return true; - } - case Value::Kind::AddrValue: { - const auto& addr = cast(*p); - CARBON_CHECK(v.value()->kind() == Value::Kind::LocationValue); - const auto& location = cast(*v.value()); - return PatternMatch( - &addr.pattern(), - ExpressionResult::Value(arena->New(location.address())), - source_loc, bindings, generic_args, trace_stream, arena); - } - case Value::Kind::VariableType: { - const auto& var_type = cast(*p); - generic_args[&var_type.binding()] = v.value(); - return true; - } - case Value::Kind::TupleType: - case Value::Kind::TupleValue: - switch (v.value()->kind()) { - case Value::Kind::TupleType: - case Value::Kind::TupleValue: { - const auto& p_tup = cast(*p); - const auto& v_tup = cast(*v.value()); - CARBON_CHECK(p_tup.elements().size() == v_tup.elements().size()); - for (size_t i = 0; i < p_tup.elements().size(); ++i) { - if (!PatternMatch(p_tup.elements()[i], - make_expr_result(v_tup.elements()[i]), source_loc, - bindings, generic_args, trace_stream, arena)) { - return false; - } - } // for - return true; - } - case Value::Kind::UninitializedValue: { - const auto& p_tup = cast(*p); - for (const auto& ele : p_tup.elements()) { - if (!PatternMatch(ele, - ExpressionResult::Value( - arena->New(ele)), - source_loc, bindings, generic_args, trace_stream, - arena)) { - return false; - } - } - return true; - } - default: - CARBON_FATAL() << "expected a tuple value in pattern, not " - << *v.value(); - } - case Value::Kind::StructValue: { - const auto& p_struct = cast(*p); - const auto& v_struct = cast(*v.value()); - CARBON_CHECK(p_struct.elements().size() == v_struct.elements().size()); - for (size_t i = 0; i < p_struct.elements().size(); ++i) { - CARBON_CHECK(p_struct.elements()[i].name == - v_struct.elements()[i].name); - if (!PatternMatch(p_struct.elements()[i].value, - ExpressionResult::Value(v_struct.elements()[i].value), - source_loc, bindings, generic_args, trace_stream, - arena)) { - return false; - } - } - return true; - } - case Value::Kind::AlternativeValue: - switch (v.value()->kind()) { - case Value::Kind::AlternativeValue: { - const auto& p_alt = cast(*p); - const auto& v_alt = cast(*v.value()); - if (&p_alt.alternative() != &v_alt.alternative()) { - return false; - } - CARBON_CHECK(p_alt.argument().has_value() == - v_alt.argument().has_value()); - if (!p_alt.argument().has_value()) { - return true; - } - return PatternMatch( - *p_alt.argument(), ExpressionResult::Value(*v_alt.argument()), - source_loc, bindings, generic_args, trace_stream, arena); - } - default: - CARBON_FATAL() << "expected a choice alternative in pattern, not " - << *v.value(); - } - case Value::Kind::UninitializedValue: - CARBON_FATAL() << "uninitialized value is not allowed in pattern " - << *v.value(); - case Value::Kind::FunctionType: - switch (v.value()->kind()) { - case Value::Kind::FunctionType: { - const auto& p_fn = cast(*p); - const auto& v_fn = cast(*v.value()); - if (!PatternMatch(&p_fn.parameters(), - ExpressionResult::Value(&v_fn.parameters()), - source_loc, bindings, generic_args, trace_stream, - arena)) { - return false; - } - if (!PatternMatch(&p_fn.return_type(), - ExpressionResult::Value(&v_fn.return_type()), - source_loc, bindings, generic_args, trace_stream, - arena)) { - return false; - } - return true; - } - default: - return false; - } - case Value::Kind::AutoType: - // `auto` matches any type, without binding any new names. We rely - // on the typechecker to ensure that `v.value()` is a type. - return true; - case Value::Kind::StaticArrayType: { - switch (v.value()->kind()) { - case Value::Kind::TupleType: - case Value::Kind::TupleValue: { - return true; - } - case Value::Kind::StaticArrayType: { - const auto& v_arr = cast(*v.value()); - return v_arr.has_size(); - } - default: - return false; - } - } - default: - return ValueEqual(p, v.value(), std::nullopt); - } -} - auto Interpreter::StepLocation() -> ErrorOr { Action& act = todo_.CurrentAction(); const Expression& exp = cast(act).expression(); diff --git a/explorer/interpreter/interpreter.h b/explorer/interpreter/interpreter.h index 829af85d0376..b312ca00e075 100644 --- a/explorer/interpreter/interpreter.h +++ b/explorer/interpreter/interpreter.h @@ -36,22 +36,6 @@ auto InterpExp(Nonnull e, Nonnull arena, Nonnull print_stream) -> ErrorOr>; -// Attempts to match `v` against the pattern `p`, returning whether matching -// is successful. If it is, populates **bindings with the variables bound by -// the match; `bindings` should only be nullopt in contexts where `p` -// is not permitted to bind variables. **bindings may be modified even if the -// match is unsuccessful, so it should typically be created for the -// PatternMatch call and then merged into an existing scope on success. -// The matches for generic variables in the pattern are output in -// `generic_args`. -// TODO: consider moving this to a separate header. -[[nodiscard]] auto PatternMatch(Nonnull p, ExpressionResult v, - SourceLocation source_loc, - std::optional> bindings, - BindingMap& generic_args, - Nonnull trace_stream, - Nonnull arena) -> bool; - } // namespace Carbon #endif // CARBON_EXPLORER_INTERPRETER_INTERPRETER_H_ diff --git a/explorer/interpreter/pattern_match.cpp b/explorer/interpreter/pattern_match.cpp new file mode 100644 index 000000000000..507ed9feda7d --- /dev/null +++ b/explorer/interpreter/pattern_match.cpp @@ -0,0 +1,220 @@ +// 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 + +#include "explorer/interpreter/pattern_match.h" + +#include "explorer/ast/value.h" +#include "explorer/common/arena.h" +#include "explorer/common/trace_stream.h" +#include "explorer/interpreter/action.h" +#include "llvm/Support/Casting.h" + +using llvm::cast; +using llvm::dyn_cast; + +namespace Carbon { + +static auto InitializePlaceholderValue(const ValueNodeView& value_node, + ExpressionResult v, + Nonnull bindings) { + switch (value_node.expression_category()) { + case ExpressionCategory::Reference: + if (v.expression_category() == ExpressionCategory::Value || + v.expression_category() == ExpressionCategory::Reference) { + // Build by copying from value or reference expression. + bindings->Initialize(value_node, v.value()); + } else { + // Location initialized by initializing expression, bind node to + // address. + CARBON_CHECK(v.address()) + << "Missing location from initializing expression"; + bindings->Bind(value_node, *v.address()); + } + break; + case ExpressionCategory::Value: + if (v.expression_category() == ExpressionCategory::Value) { + // We assume values are strictly nested for now. + bindings->BindValue(value_node, v.value()); + } else if (v.expression_category() == ExpressionCategory::Reference) { + // Bind the reference expression value directly. + CARBON_CHECK(v.address()) + << "Missing location from reference expression"; + bindings->BindAndPin(value_node, *v.address()); + } else { + // Location initialized by initializing expression, bind node to + // address. + CARBON_CHECK(v.address()) + << "Missing location from initializing expression"; + bindings->Bind(value_node, *v.address()); + } + break; + case ExpressionCategory::Initializing: + CARBON_FATAL() << "Cannot pattern match an initializing expression"; + break; + } +} + +auto PatternMatch(Nonnull p, ExpressionResult v, + SourceLocation source_loc, + std::optional> bindings, + BindingMap& generic_args, Nonnull trace_stream, + Nonnull arena) -> bool { + if (trace_stream->is_enabled()) { + *trace_stream << "match pattern " << *p << "\nfrom " + << ExpressionCategoryToString(v.expression_category()) + << " expression with value " << *v.value() << "\n"; + } + const auto make_expr_result = + [](Nonnull v) -> ExpressionResult { + if (const auto* expr_v = dyn_cast(v)) { + return ExpressionResult::Reference(expr_v->value(), expr_v->address()); + } + return ExpressionResult::Value(v); + }; + if (v.value()->kind() == Value::Kind::ReferenceExpressionValue) { + return PatternMatch(p, make_expr_result(v.value()), source_loc, bindings, + generic_args, trace_stream, arena); + } + switch (p->kind()) { + case Value::Kind::BindingPlaceholderValue: { + CARBON_CHECK(bindings.has_value()); + const auto& placeholder = cast(*p); + if (placeholder.value_node().has_value()) { + InitializePlaceholderValue(*placeholder.value_node(), v, *bindings); + } + return true; + } + case Value::Kind::AddrValue: { + const auto& addr = cast(*p); + CARBON_CHECK(v.value()->kind() == Value::Kind::LocationValue); + const auto& location = cast(*v.value()); + return PatternMatch( + &addr.pattern(), + ExpressionResult::Value(arena->New(location.address())), + source_loc, bindings, generic_args, trace_stream, arena); + } + case Value::Kind::VariableType: { + const auto& var_type = cast(*p); + generic_args[&var_type.binding()] = v.value(); + return true; + } + case Value::Kind::TupleType: + case Value::Kind::TupleValue: + switch (v.value()->kind()) { + case Value::Kind::TupleType: + case Value::Kind::TupleValue: { + const auto& p_tup = cast(*p); + const auto& v_tup = cast(*v.value()); + CARBON_CHECK(p_tup.elements().size() == v_tup.elements().size()); + for (size_t i = 0; i < p_tup.elements().size(); ++i) { + if (!PatternMatch(p_tup.elements()[i], + make_expr_result(v_tup.elements()[i]), source_loc, + bindings, generic_args, trace_stream, arena)) { + return false; + } + } // for + return true; + } + case Value::Kind::UninitializedValue: { + const auto& p_tup = cast(*p); + for (const auto& ele : p_tup.elements()) { + if (!PatternMatch(ele, + ExpressionResult::Value( + arena->New(ele)), + source_loc, bindings, generic_args, trace_stream, + arena)) { + return false; + } + } + return true; + } + default: + CARBON_FATAL() << "expected a tuple value in pattern, not " + << *v.value(); + } + case Value::Kind::StructValue: { + const auto& p_struct = cast(*p); + const auto& v_struct = cast(*v.value()); + CARBON_CHECK(p_struct.elements().size() == v_struct.elements().size()); + for (size_t i = 0; i < p_struct.elements().size(); ++i) { + CARBON_CHECK(p_struct.elements()[i].name == + v_struct.elements()[i].name); + if (!PatternMatch(p_struct.elements()[i].value, + ExpressionResult::Value(v_struct.elements()[i].value), + source_loc, bindings, generic_args, trace_stream, + arena)) { + return false; + } + } + return true; + } + case Value::Kind::AlternativeValue: + switch (v.value()->kind()) { + case Value::Kind::AlternativeValue: { + const auto& p_alt = cast(*p); + const auto& v_alt = cast(*v.value()); + if (&p_alt.alternative() != &v_alt.alternative()) { + return false; + } + CARBON_CHECK(p_alt.argument().has_value() == + v_alt.argument().has_value()); + if (!p_alt.argument().has_value()) { + return true; + } + return PatternMatch( + *p_alt.argument(), ExpressionResult::Value(*v_alt.argument()), + source_loc, bindings, generic_args, trace_stream, arena); + } + default: + CARBON_FATAL() << "expected a choice alternative in pattern, not " + << *v.value(); + } + case Value::Kind::UninitializedValue: + CARBON_FATAL() << "uninitialized value is not allowed in pattern " + << *v.value(); + case Value::Kind::FunctionType: + switch (v.value()->kind()) { + case Value::Kind::FunctionType: { + const auto& p_fn = cast(*p); + const auto& v_fn = cast(*v.value()); + if (!PatternMatch(&p_fn.parameters(), + ExpressionResult::Value(&v_fn.parameters()), + source_loc, bindings, generic_args, trace_stream, + arena)) { + return false; + } + if (!PatternMatch(&p_fn.return_type(), + ExpressionResult::Value(&v_fn.return_type()), + source_loc, bindings, generic_args, trace_stream, + arena)) { + return false; + } + return true; + } + default: + return false; + } + case Value::Kind::AutoType: + // `auto` matches any type, without binding any new names. We rely + // on the typechecker to ensure that `v.value()` is a type. + return true; + case Value::Kind::StaticArrayType: { + switch (v.value()->kind()) { + case Value::Kind::TupleType: + case Value::Kind::TupleValue: { + return true; + } + case Value::Kind::StaticArrayType: { + const auto& v_arr = cast(*v.value()); + return v_arr.has_size(); + } + default: + return false; + } + } + default: + return ValueEqual(p, v.value(), std::nullopt); + } +} +} // namespace Carbon diff --git a/explorer/interpreter/pattern_match.h b/explorer/interpreter/pattern_match.h new file mode 100644 index 000000000000..ef54c66455c4 --- /dev/null +++ b/explorer/interpreter/pattern_match.h @@ -0,0 +1,37 @@ +// 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_EXPLORER_INTERPRETER_PATTERN_MATCH_H_ +#define CARBON_EXPLORER_INTERPRETER_PATTERN_MATCH_H_ + +#include + +#include "explorer/ast/bindings.h" +#include "explorer/ast/value.h" +#include "explorer/common/nonnull.h" +#include "explorer/common/source_location.h" + +namespace Carbon { + +class RuntimeScope; +class TraceStream; +class Arena; + +// Attempts to match `v` against the pattern `p`, returning whether matching +// is successful. If it is, populates **bindings with the variables bound by +// the match; `bindings` should only be nullopt in contexts where `p` +// is not permitted to bind variables. **bindings may be modified even if the +// match is unsuccessful, so it should typically be created for the +// PatternMatch call and then merged into an existing scope on success. +// The matches for generic variables in the pattern are output in +// `generic_args`. +[[nodiscard]] auto PatternMatch(Nonnull p, ExpressionResult v, + SourceLocation source_loc, + std::optional> bindings, + BindingMap& generic_args, + Nonnull trace_stream, + Nonnull arena) -> bool; +} // namespace Carbon + +#endif // CARBON_EXPLORER_INTERPRETER_PATTERN_MATCH_H_ diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index d9f0a52c80c3..0569ac68fde7 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -32,7 +32,9 @@ #include "explorer/interpreter/impl_scope.h" #include "explorer/interpreter/interpreter.h" #include "explorer/interpreter/pattern_analysis.h" +#include "explorer/interpreter/pattern_match.h" #include "explorer/interpreter/type_structure.h" +#include "explorer/interpreter/type_utils.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" @@ -81,134 +83,6 @@ static auto ExpectPointerType(SourceLocation source_loc, return Success(); } -// Returns whether the value is a type whose values are themselves known to be -// types. -static auto IsTypeOfType(Nonnull value) -> bool { - switch (value->kind()) { - case Value::Kind::IntValue: - case Value::Kind::DestructorValue: - case Value::Kind::FunctionValue: - case Value::Kind::BoundMethodValue: - case Value::Kind::PointerValue: - case Value::Kind::LocationValue: - case Value::Kind::ReferenceExpressionValue: - case Value::Kind::BoolValue: - case Value::Kind::TupleValue: - case Value::Kind::StructValue: - case Value::Kind::NominalClassValue: - case Value::Kind::AlternativeValue: - case Value::Kind::BindingPlaceholderValue: - case Value::Kind::AddrValue: - case Value::Kind::AlternativeConstructorValue: - case Value::Kind::StringValue: - case Value::Kind::UninitializedValue: - case Value::Kind::ImplWitness: - case Value::Kind::BindingWitness: - case Value::Kind::ConstraintWitness: - case Value::Kind::ConstraintImplWitness: - case Value::Kind::ParameterizedEntityName: - case Value::Kind::MemberName: - // These are values, not types. - return false; - case Value::Kind::IntType: - case Value::Kind::BoolType: - case Value::Kind::FunctionType: - case Value::Kind::PointerType: - case Value::Kind::StructType: - case Value::Kind::NominalClassType: - case Value::Kind::MixinPseudoType: - case Value::Kind::ChoiceType: - case Value::Kind::StringType: - case Value::Kind::StaticArrayType: - case Value::Kind::TupleType: - case Value::Kind::TypeOfMixinPseudoType: - case Value::Kind::TypeOfParameterizedEntityName: - case Value::Kind::TypeOfMemberName: - case Value::Kind::TypeOfNamespaceName: - // These are types whose values are not types. - return false; - case Value::Kind::AutoType: - case Value::Kind::VariableType: - case Value::Kind::AssociatedConstant: - // A value of one of these types could be a type, but isn't known to be. - return false; - case Value::Kind::TypeType: - case Value::Kind::InterfaceType: - case Value::Kind::NamedConstraintType: - case Value::Kind::ConstraintType: - // A value of one of these types is itself always a type. - return true; - } -} - -// Returns whether the value is a type value, such as might be a valid type for -// a syntactic pattern. This includes types involving `auto`. Use -// `TypeContainsAuto` to determine if a type involves `auto`. -static auto IsType(Nonnull value) -> bool { - switch (value->kind()) { - case Value::Kind::IntValue: - case Value::Kind::FunctionValue: - case Value::Kind::DestructorValue: - case Value::Kind::BoundMethodValue: - case Value::Kind::PointerValue: - case Value::Kind::LocationValue: - case Value::Kind::ReferenceExpressionValue: - case Value::Kind::BoolValue: - case Value::Kind::TupleValue: - case Value::Kind::StructValue: - case Value::Kind::NominalClassValue: - case Value::Kind::AlternativeValue: - case Value::Kind::BindingPlaceholderValue: - case Value::Kind::AddrValue: - case Value::Kind::AlternativeConstructorValue: - case Value::Kind::StringValue: - case Value::Kind::UninitializedValue: - case Value::Kind::ImplWitness: - case Value::Kind::BindingWitness: - case Value::Kind::ConstraintWitness: - case Value::Kind::ConstraintImplWitness: - case Value::Kind::ParameterizedEntityName: - case Value::Kind::MemberName: - return false; - case Value::Kind::IntType: - case Value::Kind::BoolType: - case Value::Kind::TypeType: - case Value::Kind::PointerType: - case Value::Kind::FunctionType: - case Value::Kind::StructType: - case Value::Kind::TupleType: - case Value::Kind::NominalClassType: - case Value::Kind::InterfaceType: - case Value::Kind::NamedConstraintType: - case Value::Kind::ConstraintType: - case Value::Kind::ChoiceType: - case Value::Kind::VariableType: - case Value::Kind::StringType: - case Value::Kind::StaticArrayType: - case Value::Kind::AutoType: - return true; - case Value::Kind::TypeOfParameterizedEntityName: - case Value::Kind::TypeOfMemberName: - case Value::Kind::TypeOfMixinPseudoType: - case Value::Kind::TypeOfNamespaceName: - // These aren't first-class types, but they are still types. - return true; - case Value::Kind::AssociatedConstant: { - // An associated type is an associated constant whose type is a - // type-of-type. - const auto& assoc = cast(*value); - // TODO: Should we substitute in the arguments? Given - // interface I(T:! type) { let V:! T; } - // ... is T.(I(type).V) considered to be a type? - return IsTypeOfType(&assoc.constant().static_type()); - } - case Value::Kind::MixinPseudoType: - // Mixin type is a second-class type that cannot be used - // within a type annotation expression. - return false; - } -} - // Expect that a type is complete. Issue a diagnostic if not. static auto ExpectCompleteType(SourceLocation source_loc, std::string_view context, @@ -323,76 +197,6 @@ static auto ExpectConcreteType(SourceLocation source_loc, return Success(); } -// Returns whether *value represents the type of a Carbon value, as -// opposed to a type pattern or a non-type value. -static auto TypeIsDeduceable(Nonnull type) -> bool { - CARBON_CHECK(IsType(type)) << "expected a type, but found " << *type; - - switch (type->kind()) { - case Value::Kind::IntValue: - case Value::Kind::FunctionValue: - case Value::Kind::DestructorValue: - case Value::Kind::BoundMethodValue: - case Value::Kind::PointerValue: - case Value::Kind::LocationValue: - case Value::Kind::ReferenceExpressionValue: - case Value::Kind::BoolValue: - case Value::Kind::TupleValue: - case Value::Kind::StructValue: - case Value::Kind::NominalClassValue: - case Value::Kind::AlternativeValue: - case Value::Kind::BindingPlaceholderValue: - case Value::Kind::AddrValue: - case Value::Kind::AlternativeConstructorValue: - case Value::Kind::StringValue: - case Value::Kind::UninitializedValue: - case Value::Kind::ImplWitness: - case Value::Kind::BindingWitness: - case Value::Kind::ConstraintWitness: - case Value::Kind::ConstraintImplWitness: - case Value::Kind::ParameterizedEntityName: - case Value::Kind::MemberName: - case Value::Kind::MixinPseudoType: - CARBON_FATAL() << "non-type value"; - case Value::Kind::IntType: - case Value::Kind::BoolType: - case Value::Kind::TypeType: - case Value::Kind::VariableType: - case Value::Kind::StringType: - case Value::Kind::TypeOfMixinPseudoType: - case Value::Kind::TypeOfParameterizedEntityName: - case Value::Kind::TypeOfMemberName: - case Value::Kind::TypeOfNamespaceName: - // These types do not contain other types. - return false; - case Value::Kind::FunctionType: - case Value::Kind::NominalClassType: - case Value::Kind::InterfaceType: - case Value::Kind::NamedConstraintType: - case Value::Kind::ConstraintType: - case Value::Kind::ChoiceType: - case Value::Kind::AssociatedConstant: - // These types can contain other types, but those types can't involve - // `auto`. - return false; - case Value::Kind::AutoType: - return true; - case Value::Kind::StructType: - return llvm::any_of( - llvm::map_range(cast(type)->fields(), - [](const NamedValue& v) { return v.value; }), - TypeIsDeduceable); - case Value::Kind::TupleType: - return llvm::any_of(cast(type)->elements(), TypeIsDeduceable); - case Value::Kind::PointerType: - return TypeIsDeduceable(&cast(type)->pointee_type()); - case Value::Kind::StaticArrayType: - const auto* array_type = cast(type); - return !array_type->has_size() || - TypeIsDeduceable(&array_type->element_type()); - } -} - // Returns whether `type` is a placeholder type, which is a second-class type // that cannot be the type of a binding but can be the type of an expression. static auto IsPlaceholderType(Nonnull type) -> bool { @@ -401,12 +205,6 @@ static auto IsPlaceholderType(Nonnull type) -> bool { TypeOfMixinPseudoType, TypeOfNamespaceName>(type); } -// Returns whether `value` is a concrete type, which would be valid as the -// static type of an expression. This is currently any type other than `auto`. -static auto IsNonDeduceableType(Nonnull value) -> bool { - return IsType(value) && !TypeIsDeduceable(value); -} - static auto ExpectResolvedBindingType(const BindingPattern& binding, Nonnull type) -> ErrorOr { @@ -4459,24 +4257,6 @@ auto TypeChecker::TypeCheckWhereClause(Nonnull clause, } } -// Returns the list size for type deduction. -static auto GetSize(Nonnull from) -> size_t { - switch (from->kind()) { - case Value::Kind::TupleType: - case Value::Kind::TupleValue: { - const auto& from_tup = cast(*from); - return from_tup.elements().size(); - } - case Value::Kind::StaticArrayType: { - const auto& from_arr = cast(*from); - CARBON_CHECK(from_arr.has_size()); - return from_arr.size(); - } - default: - return 0; - } -} - auto TypeChecker::TypeCheckPattern( Nonnull p, PatternRequirements requirements, std::optional> expected, ImplScope& impl_scope, diff --git a/explorer/interpreter/type_utils.cpp b/explorer/interpreter/type_utils.cpp new file mode 100644 index 000000000000..8b6fb981a5e3 --- /dev/null +++ b/explorer/interpreter/type_utils.cpp @@ -0,0 +1,225 @@ +// 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 + +#include "explorer/interpreter/type_utils.h" + +#include "explorer/ast/value.h" +#include "llvm/Support/Casting.h" + +using llvm::cast; + +namespace Carbon { + +auto IsNonDeduceableType(Nonnull value) -> bool { + return IsType(value) && !TypeIsDeduceable(value); +} + +auto IsType(Nonnull value) -> bool { + switch (value->kind()) { + case Value::Kind::IntValue: + case Value::Kind::FunctionValue: + case Value::Kind::DestructorValue: + case Value::Kind::BoundMethodValue: + case Value::Kind::PointerValue: + case Value::Kind::LocationValue: + case Value::Kind::ReferenceExpressionValue: + case Value::Kind::BoolValue: + case Value::Kind::TupleValue: + case Value::Kind::StructValue: + case Value::Kind::NominalClassValue: + case Value::Kind::AlternativeValue: + case Value::Kind::BindingPlaceholderValue: + case Value::Kind::AddrValue: + case Value::Kind::AlternativeConstructorValue: + case Value::Kind::StringValue: + case Value::Kind::UninitializedValue: + case Value::Kind::ImplWitness: + case Value::Kind::BindingWitness: + case Value::Kind::ConstraintWitness: + case Value::Kind::ConstraintImplWitness: + case Value::Kind::ParameterizedEntityName: + case Value::Kind::MemberName: + return false; + case Value::Kind::IntType: + case Value::Kind::BoolType: + case Value::Kind::TypeType: + case Value::Kind::PointerType: + case Value::Kind::FunctionType: + case Value::Kind::StructType: + case Value::Kind::TupleType: + case Value::Kind::NominalClassType: + case Value::Kind::InterfaceType: + case Value::Kind::NamedConstraintType: + case Value::Kind::ConstraintType: + case Value::Kind::ChoiceType: + case Value::Kind::VariableType: + case Value::Kind::StringType: + case Value::Kind::StaticArrayType: + case Value::Kind::AutoType: + return true; + case Value::Kind::TypeOfParameterizedEntityName: + case Value::Kind::TypeOfMemberName: + case Value::Kind::TypeOfMixinPseudoType: + case Value::Kind::TypeOfNamespaceName: + // These aren't first-class types, but they are still types. + return true; + case Value::Kind::AssociatedConstant: { + // An associated type is an associated constant whose type is a + // type-of-type. + const auto& assoc = cast(*value); + // TODO: Should we substitute in the arguments? Given + // interface I(T:! type) { let V:! T; } + // ... is T.(I(type).V) considered to be a type? + return IsTypeOfType(&assoc.constant().static_type()); + } + case Value::Kind::MixinPseudoType: + // Mixin type is a second-class type that cannot be used + // within a type annotation expression. + return false; + } +} + +auto TypeIsDeduceable(Nonnull type) -> bool { + CARBON_CHECK(IsType(type)) << "expected a type, but found " << *type; + + switch (type->kind()) { + case Value::Kind::IntValue: + case Value::Kind::FunctionValue: + case Value::Kind::DestructorValue: + case Value::Kind::BoundMethodValue: + case Value::Kind::PointerValue: + case Value::Kind::LocationValue: + case Value::Kind::ReferenceExpressionValue: + case Value::Kind::BoolValue: + case Value::Kind::TupleValue: + case Value::Kind::StructValue: + case Value::Kind::NominalClassValue: + case Value::Kind::AlternativeValue: + case Value::Kind::BindingPlaceholderValue: + case Value::Kind::AddrValue: + case Value::Kind::AlternativeConstructorValue: + case Value::Kind::StringValue: + case Value::Kind::UninitializedValue: + case Value::Kind::ImplWitness: + case Value::Kind::BindingWitness: + case Value::Kind::ConstraintWitness: + case Value::Kind::ConstraintImplWitness: + case Value::Kind::ParameterizedEntityName: + case Value::Kind::MemberName: + case Value::Kind::MixinPseudoType: + CARBON_FATAL() << "non-type value"; + case Value::Kind::IntType: + case Value::Kind::BoolType: + case Value::Kind::TypeType: + case Value::Kind::VariableType: + case Value::Kind::StringType: + case Value::Kind::TypeOfMixinPseudoType: + case Value::Kind::TypeOfParameterizedEntityName: + case Value::Kind::TypeOfMemberName: + case Value::Kind::TypeOfNamespaceName: + // These types do not contain other types. + return false; + case Value::Kind::FunctionType: + case Value::Kind::NominalClassType: + case Value::Kind::InterfaceType: + case Value::Kind::NamedConstraintType: + case Value::Kind::ConstraintType: + case Value::Kind::ChoiceType: + case Value::Kind::AssociatedConstant: + // These types can contain other types, but those types can't involve + // `auto`. + return false; + case Value::Kind::AutoType: + return true; + case Value::Kind::StructType: + return llvm::any_of( + llvm::map_range(cast(type)->fields(), + [](const NamedValue& v) { return v.value; }), + TypeIsDeduceable); + case Value::Kind::TupleType: + return llvm::any_of(cast(type)->elements(), TypeIsDeduceable); + case Value::Kind::PointerType: + return TypeIsDeduceable(&cast(type)->pointee_type()); + case Value::Kind::StaticArrayType: + const auto* array_type = cast(type); + return !array_type->has_size() || + TypeIsDeduceable(&array_type->element_type()); + } +} + +auto GetSize(Nonnull from) -> size_t { + switch (from->kind()) { + case Value::Kind::TupleType: + case Value::Kind::TupleValue: { + const auto& from_tup = cast(*from); + return from_tup.elements().size(); + } + case Value::Kind::StaticArrayType: { + const auto& from_arr = cast(*from); + CARBON_CHECK(from_arr.has_size()); + return from_arr.size(); + } + default: + return 0; + } +} + +auto IsTypeOfType(Nonnull value) -> bool { + switch (value->kind()) { + case Value::Kind::IntValue: + case Value::Kind::DestructorValue: + case Value::Kind::FunctionValue: + case Value::Kind::BoundMethodValue: + case Value::Kind::PointerValue: + case Value::Kind::LocationValue: + case Value::Kind::ReferenceExpressionValue: + case Value::Kind::BoolValue: + case Value::Kind::TupleValue: + case Value::Kind::StructValue: + case Value::Kind::NominalClassValue: + case Value::Kind::AlternativeValue: + case Value::Kind::BindingPlaceholderValue: + case Value::Kind::AddrValue: + case Value::Kind::AlternativeConstructorValue: + case Value::Kind::StringValue: + case Value::Kind::UninitializedValue: + case Value::Kind::ImplWitness: + case Value::Kind::BindingWitness: + case Value::Kind::ConstraintWitness: + case Value::Kind::ConstraintImplWitness: + case Value::Kind::ParameterizedEntityName: + case Value::Kind::MemberName: + // These are values, not types. + return false; + case Value::Kind::IntType: + case Value::Kind::BoolType: + case Value::Kind::FunctionType: + case Value::Kind::PointerType: + case Value::Kind::StructType: + case Value::Kind::NominalClassType: + case Value::Kind::MixinPseudoType: + case Value::Kind::ChoiceType: + case Value::Kind::StringType: + case Value::Kind::StaticArrayType: + case Value::Kind::TupleType: + case Value::Kind::TypeOfMixinPseudoType: + case Value::Kind::TypeOfParameterizedEntityName: + case Value::Kind::TypeOfMemberName: + case Value::Kind::TypeOfNamespaceName: + // These are types whose values are not types. + return false; + case Value::Kind::AutoType: + case Value::Kind::VariableType: + case Value::Kind::AssociatedConstant: + // A value of one of these types could be a type, but isn't known to be. + return false; + case Value::Kind::TypeType: + case Value::Kind::InterfaceType: + case Value::Kind::NamedConstraintType: + case Value::Kind::ConstraintType: + // A value of one of these types is itself always a type. + return true; + } +} +} // namespace Carbon diff --git a/explorer/interpreter/type_utils.h b/explorer/interpreter/type_utils.h new file mode 100644 index 000000000000..e951e56f0f08 --- /dev/null +++ b/explorer/interpreter/type_utils.h @@ -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_EXPLORER_INTERPRETER_TYPE_UTILS_H_ +#define CARBON_EXPLORER_INTERPRETER_TYPE_UTILS_H_ + +#include "explorer/common/nonnull.h" + +namespace Carbon { + +class Value; + +// Returns whether `value` is a concrete type, which would be valid as the +// static type of an expression. This is currently any type other than `auto`. +auto IsNonDeduceableType(Nonnull value) -> bool; + +// Returns whether the value is a type value, such as might be a valid type for +// a syntactic pattern. This includes types involving `auto`. Use +// `TypeContainsAuto` to determine if a type involves `auto`. +auto IsType(Nonnull value) -> bool; + +// Returns whether *value represents the type of a Carbon value, as +// opposed to a type pattern or a non-type value. +auto TypeIsDeduceable(Nonnull type) -> bool; + +// Returns the list size for type deduction. +auto GetSize(Nonnull from) -> size_t; + +// Returns whether the value is a type whose values are themselves known to be +// types. +auto IsTypeOfType(Nonnull value) -> bool; +} // namespace Carbon + +#endif // CARBON_EXPLORER_INTERPRETER_TYPE_UTILS_H_