diff --git a/common/error.h b/common/error.h index 3975cde03e70..574c46643623 100644 --- a/common/error.h +++ b/common/error.h @@ -36,6 +36,12 @@ class [[nodiscard]] Error { : location_(std::move(other.location_)), message_(std::move(other.message_)) {} + Error& operator=(Error&& other) noexcept { + location_ = std::move(other.location_); + message_ = std::move(other.message_); + return *this; + } + // Prints the error string. void Print(llvm::raw_ostream& out) const { if (!location().empty()) { @@ -73,9 +79,6 @@ class [[nodiscard]] ErrorOr { // NOLINTNEXTLINE(google-explicit-constructor) ErrorOr(T val) : val_(std::move(val)) {} - // Moves held state. - ErrorOr(ErrorOr&& other) noexcept : val_(std::move(other.val_)) {} - // Returns true for success. auto ok() const -> bool { return std::holds_alternative(val_); } diff --git a/explorer/ast/value_transform.h b/explorer/ast/value_transform.h index 0a2488fd63af..58f1078d5721 100644 --- a/explorer/ast/value_transform.h +++ b/explorer/ast/value_transform.h @@ -5,6 +5,7 @@ #ifndef CARBON_EXPLORER_AST_VALUE_TRANSFORM_H_ #define CARBON_EXPLORER_AST_VALUE_TRANSFORM_H_ +#include "common/error.h" #include "explorer/ast/value.h" namespace Carbon { @@ -40,17 +41,61 @@ class TransformBase { public: explicit TransformBase(Nonnull arena) : arena_(arena) {} + // Transform the given value, and produce either the transformed value or an + // error. template - auto Transform(T&& v) -> decltype(auto) { - return static_cast(*this)(std::forward(v)); + auto Transform(const T& v) -> ErrorOr { + auto result = TransformOrOriginal(v); + if (!status_.ok()) { + Error error = std::move(status_).error(); + status_ = Success(); + return error; + } + return result; + } + + protected: + // Given an original value and the result of calling `operator()`, find the + // transformed value we should use. + // + // If `operator()` returns `ErrorOr`, then on failure, collect the error + // and return the untransformed value; otherwise, return the transformed + // value. + template + auto CollectError(const T& /*original*/, const U& transformed) -> U { + return transformed; + } + template + auto CollectError(const T& original, ErrorOr transformed) -> U { + if (!transformed.ok()) { + status_ = std::move(transformed).error(); + return original; + } + return std::move(*transformed); + } + + // Transform the given value, or return the original if transformation fails. + template + auto TransformOrOriginal(const T& v) + -> decltype(CollectError(v, std::declval()(v))) { + // If we've already failed, don't do any more transformations. + if (!status_.ok()) { + return v; + } + return CollectError(v, static_cast(*this)(v)); } // Transformable values are recursively transformed by default. template , void*> = nullptr> auto operator()(const T& value) -> T { - return value.Decompose([&](auto&&... elements) { - return T{Transform(decltype(elements)(elements))...}; + return value.Decompose([&](const auto&... elements) { + return [&](auto&&... transformed_elements) { + if (status_.ok()) { + return T{decltype(transformed_elements)(transformed_elements)...}; + } + return value; + }(TransformOrOriginal(elements)...); }); } @@ -59,9 +104,17 @@ class TransformBase { template , void*> = nullptr> auto operator()(Nonnull value) -> auto{ - return value->Decompose([&](auto&&... elements) { - return AllocateTrait::New(arena_, - Transform(decltype(elements)(elements))...); + return value->Decompose([&](const auto&... elements) { + return [&](auto&&... transformed_elements) + -> decltype(AllocateTrait::New( + arena_, + decltype(transformed_elements)(transformed_elements)...)) { + if (status_.ok()) { + return AllocateTrait::New( + arena_, decltype(transformed_elements)(transformed_elements)...); + } + return value; + }(TransformOrOriginal(elements)...); }); } @@ -79,13 +132,14 @@ class TransformBase { if (!v) { return std::nullopt; } - return Transform(*v); + return TransformOrOriginal(*v); } // Transform `pair` by transforming T and U. template auto operator()(const std::pair& pair) -> std::pair { - return std::pair{Transform(pair.first), Transform(pair.second)}; + return std::pair{TransformOrOriginal(pair.first), + TransformOrOriginal(pair.second)}; } // Transform `vector` by transforming its elements. @@ -94,7 +148,7 @@ class TransformBase { std::vector result; result.reserve(vec.size()); for (auto& value : vec) { - result.push_back(Transform(value)); + result.push_back(TransformOrOriginal(value)); } return result; } @@ -104,7 +158,7 @@ class TransformBase { auto operator()(const std::map& map) -> std::map { std::map result; for (auto& [key, value] : map) { - result.insert({Transform(key), Transform(value)}); + result.insert({TransformOrOriginal(key), TransformOrOriginal(value)}); } return result; } @@ -114,13 +168,17 @@ class TransformBase { auto operator()(const llvm::StringMap& map) -> llvm::StringMap { llvm::StringMap result; for (const auto& it : map) { - result.insert({Transform(it.first()), Transform(it.second)}); + result.insert( + {TransformOrOriginal(it.first()), TransformOrOriginal(it.second)}); } return result; } private: Nonnull arena_; + // Temporary storage for an error that was produced during transformation + // that has not yet been handed back to the caller. + ErrorOr status_ = Success(); }; // Base class for transforms of `Value`s. @@ -159,7 +217,7 @@ class ValueTransform : public TransformBase { return value->template Visit([&](const auto* derived_value) { using DerivedType = std::remove_pointer_t; static_assert(IsRecursivelyTransformable); - return this->Transform(derived_value); + return this->TransformOrOriginal(derived_value); }); } @@ -170,7 +228,8 @@ class ValueTransform : public TransformBase { // Provide a more precise type from transforming a `Witness`. auto operator()(Nonnull value) -> Nonnull { - return llvm::cast(this->Transform(llvm::cast(value))); + return llvm::cast( + this->TransformOrOriginal(llvm::cast(value))); } // For elements, dispatch on the element kind and recursively transform. diff --git a/explorer/interpreter/impl_scope.cpp b/explorer/interpreter/impl_scope.cpp index 09489ddb1dec..bdf676f826e0 100644 --- a/explorer/interpreter/impl_scope.cpp +++ b/explorer/interpreter/impl_scope.cpp @@ -121,8 +121,9 @@ auto ImplScope::TryResolve(Nonnull constraint_type, bool diagnose_missing_impl) const -> ErrorOr>> { if (const auto* iface_type = dyn_cast(constraint_type)) { - iface_type = - cast(type_checker.Substitute(bindings, iface_type)); + CARBON_ASSIGN_OR_RETURN( + iface_type, + type_checker.SubstituteCast(bindings, iface_type)); return TryResolveInterface(iface_type, impl_type, source_loc, type_checker, diagnose_missing_impl); } @@ -144,13 +145,17 @@ auto ImplScope::TryResolve(Nonnull constraint_type, } Bindings local_bindings = bindings; local_bindings.Add(constraint->self_binding(), impl_type, witness); + + CARBON_ASSIGN_OR_RETURN(const auto* subst_interface, + type_checker.SubstituteCast( + local_bindings, impl.interface)); + CARBON_ASSIGN_OR_RETURN( + Nonnull subst_type, + type_checker.Substitute(local_bindings, impl.type)); CARBON_ASSIGN_OR_RETURN( std::optional> result, - TryResolveInterface( - cast( - type_checker.Substitute(local_bindings, impl.interface)), - type_checker.Substitute(local_bindings, impl.type), source_loc, - type_checker, diagnose_missing_impl)); + TryResolveInterface(subst_interface, subst_type, source_loc, + type_checker, diagnose_missing_impl)); if (!result) { return {std::nullopt}; } @@ -174,16 +179,22 @@ auto ImplScope::TryResolve(Nonnull constraint_type, local_bindings.Add(constraint->self_binding(), impl_type, witness); SingleStepEqualityContext equality_ctx(this); for (const auto& intrinsic : intrinsics) { + CARBON_ASSIGN_OR_RETURN( + Nonnull type, + type_checker.Substitute(local_bindings, intrinsic.type)); IntrinsicConstraint converted = { - .type = type_checker.Substitute(local_bindings, intrinsic.type), - .kind = intrinsic.kind, - .arguments = {}}; + .type = type, .kind = intrinsic.kind, .arguments = {}}; converted.arguments.reserve(intrinsic.arguments.size()); for (Nonnull argument : intrinsic.arguments) { - converted.arguments.push_back( + CARBON_ASSIGN_OR_RETURN( + Nonnull subst_arg, type_checker.Substitute(local_bindings, argument)); + converted.arguments.push_back(subst_arg); } - if (!type_checker.IsIntrinsicConstraintSatisfied(converted, *this)) { + CARBON_ASSIGN_OR_RETURN( + bool intrinsic_satisfied, + type_checker.IsIntrinsicConstraintSatisfied(converted, *this)); + if (!intrinsic_satisfied) { if (!diagnose_missing_impl) { return {std::nullopt}; } @@ -193,11 +204,11 @@ auto ImplScope::TryResolve(Nonnull constraint_type, } for (const auto& equal : equals) { auto it = equal.values.begin(); - Nonnull first = - type_checker.Substitute(local_bindings, *it++); + CARBON_ASSIGN_OR_RETURN(Nonnull first, + type_checker.Substitute(local_bindings, *it++)); for (; it != equal.values.end(); ++it) { - Nonnull current = - type_checker.Substitute(local_bindings, *it); + CARBON_ASSIGN_OR_RETURN(Nonnull current, + type_checker.Substitute(local_bindings, *it)); if (!ValueEqual(first, current, &equality_ctx)) { if (!diagnose_missing_impl) { return {std::nullopt}; @@ -208,10 +219,13 @@ auto ImplScope::TryResolve(Nonnull constraint_type, } } for (const auto& rewrite : rewrites) { - Nonnull constant = - type_checker.Substitute(local_bindings, rewrite.constant); - Nonnull value = type_checker.Substitute( - local_bindings, rewrite.converted_replacement); + CARBON_ASSIGN_OR_RETURN( + Nonnull constant, + type_checker.Substitute(local_bindings, rewrite.constant)); + CARBON_ASSIGN_OR_RETURN( + Nonnull value, + type_checker.Substitute(local_bindings, + rewrite.converted_replacement)); if (!ValueEqual(constant, value, &equality_ctx)) { if (!diagnose_missing_impl) { return {std::nullopt}; diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 9dd1285cdfb7..c72966558a6b 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -408,7 +408,7 @@ static auto FindField(llvm::ArrayRef fields, auto TypeChecker::FieldTypesImplicitlyConvertible( llvm::ArrayRef source_fields, llvm::ArrayRef destination_fields, - const ImplScope& impl_scope) const -> bool { + const ImplScope& impl_scope) const -> ErrorOr { // TODO: If default fields are implemented, the // code must be adapted to skip them. // Ensure every field name exists in the destination. @@ -420,14 +420,19 @@ auto TypeChecker::FieldTypesImplicitlyConvertible( for (const auto& source_field : source_fields) { std::optional destination_field = FindField(destination_fields, source_field.name); - if (!destination_field.has_value() || - !IsImplicitlyConvertible(source_field.value, - destination_field.value().value, impl_scope, - // TODO: We don't have a way to perform - // user-defined conversions of a struct field - // yet, because we can't write a suitable impl - // for ImplicitAs. - /*allow_user_defined_conversions=*/false)) { + if (!destination_field.has_value()) { + return false; + } + CARBON_ASSIGN_OR_RETURN( + bool convertible, + IsImplicitlyConvertible(source_field.value, + destination_field.value().value, impl_scope, + // TODO: We don't have a way to perform + // user-defined conversions of a struct field + // yet, because we can't write a suitable impl + // for ImplicitAs. + /*allow_user_defined_conversions=*/false)); + if (!convertible) { return false; } } @@ -435,14 +440,15 @@ auto TypeChecker::FieldTypesImplicitlyConvertible( } auto TypeChecker::FieldTypes(const NominalClassType& class_type) const - -> std::vector { + -> ErrorOr> { std::vector field_types; for (Nonnull m : class_type.declaration().members()) { switch (m->kind()) { case DeclarationKind::VariableDeclaration: { const auto& var = cast(*m); - Nonnull field_type = - Substitute(class_type.bindings(), &var.binding().static_type()); + CARBON_ASSIGN_OR_RETURN( + Nonnull field_type, + Substitute(class_type.bindings(), &var.binding().static_type())); field_types.push_back({var.binding().name(), field_type}); break; } @@ -454,10 +460,11 @@ auto TypeChecker::FieldTypes(const NominalClassType& class_type) const } auto TypeChecker::FieldTypesWithBase(const NominalClassType& class_type) const - -> std::vector { - auto fields = FieldTypes(class_type); + -> ErrorOr> { + CARBON_ASSIGN_OR_RETURN(auto fields, FieldTypes(class_type)); if (const auto base_type = class_type.base()) { - auto base_fields = FieldTypesWithBase(*base_type.value()); + CARBON_ASSIGN_OR_RETURN(auto base_fields, + FieldTypesWithBase(*base_type.value())); fields.emplace_back(NamedValue{std::string(NominalClassValue::BaseField), base_type.value()}); } @@ -467,7 +474,7 @@ auto TypeChecker::FieldTypesWithBase(const NominalClassType& class_type) const auto TypeChecker::IsImplicitlyConvertible( Nonnull source, Nonnull destination, const ImplScope& impl_scope, bool allow_user_defined_conversions) const - -> bool { + -> ErrorOr { // Check for an exact match or for an implicit conversion. // TODO: `impl`s of `ImplicitAs` should be provided to cover these // conversions. @@ -480,22 +487,31 @@ auto TypeChecker::IsImplicitlyConvertible( switch (source->kind()) { case Value::Kind::StructType: switch (destination->kind()) { - case Value::Kind::StructType: - if (FieldTypesImplicitlyConvertible( + case Value::Kind::StructType: { + CARBON_ASSIGN_OR_RETURN( + bool fields_convertible, + FieldTypesImplicitlyConvertible( cast(*source).fields(), - cast(*destination).fields(), impl_scope)) { + cast(*destination).fields(), impl_scope)); + if (fields_convertible) { return true; } break; - case Value::Kind::NominalClassType: - if (IsImplicitlyConvertible( - source, - arena_->New( - FieldTypesWithBase(cast(*destination))), - impl_scope, allow_user_defined_conversions)) { + } + case Value::Kind::NominalClassType: { + CARBON_ASSIGN_OR_RETURN( + auto field_types, + FieldTypesWithBase(cast(*destination))); + CARBON_ASSIGN_OR_RETURN( + bool convertible, + IsImplicitlyConvertible( + source, arena_->New(field_types), impl_scope, + allow_user_defined_conversions)); + if (convertible) { return true; } break; + } case Value::Kind::TypeType: case Value::Kind::InterfaceType: case Value::Kind::NamedConstraintType: @@ -520,9 +536,12 @@ auto TypeChecker::IsImplicitlyConvertible( } bool all_ok = true; for (size_t i = 0; i < source_tuple.elements().size(); ++i) { - if (!IsImplicitlyConvertible( + CARBON_ASSIGN_OR_RETURN( + bool convertible, + IsImplicitlyConvertible( source_tuple.elements()[i], destination_tuple.elements()[i], - impl_scope, /*allow_user_defined_conversions=*/false)) { + impl_scope, /*allow_user_defined_conversions=*/false)); + if (!convertible) { all_ok = false; break; } @@ -539,9 +558,12 @@ auto TypeChecker::IsImplicitlyConvertible( } bool all_ok = true; for (Nonnull source_element : source_tuple.elements()) { - if (!IsImplicitlyConvertible( + CARBON_ASSIGN_OR_RETURN( + bool convertible, + IsImplicitlyConvertible( source_element, &destination_array.element_type(), - impl_scope, /*allow_user_defined_conversions=*/false)) { + impl_scope, /*allow_user_defined_conversions=*/false)); + if (!convertible) { all_ok = false; break; } @@ -558,9 +580,12 @@ auto TypeChecker::IsImplicitlyConvertible( // A tuple value converts to a type if all of its fields do. bool all_types = true; for (Nonnull source_element : source_tuple.elements()) { - if (!IsImplicitlyConvertible( + CARBON_ASSIGN_OR_RETURN( + bool convertible, + IsImplicitlyConvertible( source_element, destination, impl_scope, - /*allow_user_defined_conversions=*/false)) { + /*allow_user_defined_conversions=*/false)); + if (!convertible) { all_types = false; break; } @@ -660,8 +685,11 @@ auto TypeChecker::ImplicitlyConvert(std::string_view context, // TODO: This doesn't work for cases of combined built-in and user-defined // conversion, such as converting a struct element via an `ImplicitAs` impl. - if (IsImplicitlyConvertible(source_type, destination, impl_scope, - /*allow_user_defined_conversions=*/false)) { + CARBON_ASSIGN_OR_RETURN( + bool convertible, + IsImplicitlyConvertible(source_type, destination, impl_scope, + /*allow_user_defined_conversions=*/false)); + if (convertible) { // A type only implicitly converts to a constraint if there is an impl of // that constraint for that type in scope. // TODO: Instead of excluding the special case where the destination is @@ -737,7 +765,7 @@ auto TypeChecker::ImplicitlyConvert(std::string_view context, auto TypeChecker::IsIntrinsicConstraintSatisfied( const IntrinsicConstraint& constraint, const ImplScope& impl_scope) const - -> bool { + -> ErrorOr { // TODO: Check to see if this constraint is known in the current impl scope. switch (constraint.kind) { case IntrinsicConstraint::ImplicitAs: @@ -1209,21 +1237,23 @@ auto TypeChecker::ArgumentDeduction::Finish( << binding->name() << " in " << context_; } - const Value* binding_type = - type_checker.Substitute(bindings, &binding->static_type()); - const Value* substituted_type = - type_checker.Substitute(bindings, binding_type); + CARBON_ASSIGN_OR_RETURN( + const Value* binding_type, + type_checker.Substitute(bindings, &binding->static_type())); const auto* first_value = values[0]; for (const auto* value : values) { // TODO: It's not clear that conversions are or should be possible here. // If they are permitted, we should allow user-defined conversions, and // actually perform the conversion. - if (!IsTypeOfType(substituted_type) && - !type_checker.IsImplicitlyConvertible(value, substituted_type, - impl_scope, false)) { - return ProgramError(source_loc_) - << "cannot convert deduced value " << *value << " for " - << binding->name() << " to parameter type " << *substituted_type; + if (!IsTypeOfType(binding_type)) { + CARBON_ASSIGN_OR_RETURN(bool convertible, + type_checker.IsImplicitlyConvertible( + value, binding_type, impl_scope, false)); + if (!convertible) { + return ProgramError(source_loc_) + << "cannot convert deduced value " << *value << " for " + << binding->name() << " to parameter type " << *binding_type; + } } // All deductions are required to produce the same value. Note that we @@ -1265,8 +1295,8 @@ auto TypeChecker::ArgumentDeduction::Finish( // Form the binding's resolved type and convert the argument expression to // it. const Value* binding_type = &binding->static_type(); - const Value* substituted_type = - type_checker.Substitute(bindings, binding_type); + CARBON_ASSIGN_OR_RETURN(const Value* substituted_type, + type_checker.Substitute(bindings, binding_type)); CARBON_ASSIGN_OR_RETURN( arg, type_checker.ImplicitlyConvert(context_, impl_scope, arg, substituted_type)); @@ -1296,13 +1326,15 @@ auto TypeChecker::ArgumentDeduction::Finish( // Check non-deduced potential mismatches now we can substitute into them. for (const auto& mismatch : non_deduced_mismatches_) { - const Value* subst_param = - type_checker.Substitute(bindings, mismatch.param); + CARBON_ASSIGN_OR_RETURN(const Value* subst_param, + type_checker.Substitute(bindings, mismatch.param)); bool type = IsType(subst_param) && IsType(mismatch.arg); if (type && mismatch.allow_implicit_conversion) { - if (!type_checker.IsImplicitlyConvertible(mismatch.arg, subst_param, - impl_scope, true)) { + CARBON_ASSIGN_OR_RETURN(bool convertible, + type_checker.IsImplicitlyConvertible( + mismatch.arg, subst_param, impl_scope, true)); + if (!convertible) { if (!diagnose_deduction_failure) { return {std::nullopt}; } @@ -1488,11 +1520,12 @@ class TypeChecker::ConstraintTypeBuilder { // constraint's self binding. The `self_witness` is the witness for the // resulting constraint, and can be `GetSelfWitness()`. The `bindings` // parameter specifies any additional substitutions to perform. - void AddAndSubstitute(const TypeChecker& type_checker, + auto AddAndSubstitute(const TypeChecker& type_checker, Nonnull constraint, Nonnull self, Nonnull self_witness, - const Bindings& bindings, bool add_lookup_contexts) { + const Bindings& bindings, bool add_lookup_contexts) + -> ErrorOr { if (type_checker.trace_stream_->is_enabled()) { *type_checker.trace_stream_ << "merging " << *constraint << " into constraint with " @@ -1506,11 +1539,13 @@ class TypeChecker::ConstraintTypeBuilder { Bindings local_bindings = bindings; local_bindings.Add(constraint->self_binding(), self, type_checker.MakeConstraintWitness(witnesses)); - int index = AddImplConstraint( - {.type = - type_checker.Substitute(local_bindings, impl_constraint.type), - .interface = cast(type_checker.Substitute( - local_bindings, impl_constraint.interface))}); + CARBON_ASSIGN_OR_RETURN( + Nonnull type, + type_checker.Substitute(local_bindings, impl_constraint.type)); + CARBON_ASSIGN_OR_RETURN(const auto* interface, + type_checker.SubstituteCast( + local_bindings, impl_constraint.interface)); + int index = AddImplConstraint({.type = type, .interface = interface}); witnesses.push_back( type_checker.MakeConstraintWitnessAccess(self_witness, index)); } @@ -1527,10 +1562,14 @@ class TypeChecker::ConstraintTypeBuilder { // the resulting constraint. Otherwise, discard the rewrites and keep only // their corresponding equality constraints. for (const auto& rewrite_constraint : constraint->rewrite_constraints()) { - const auto* interface = cast(type_checker.Substitute( - local_bindings, &rewrite_constraint.constant->interface())); - Nonnull converted_value = type_checker.Substitute( - local_bindings, rewrite_constraint.converted_replacement); + CARBON_ASSIGN_OR_RETURN( + const auto* interface, + type_checker.SubstituteCast( + local_bindings, &rewrite_constraint.constant->interface())); + CARBON_ASSIGN_OR_RETURN( + Nonnull converted_value, + type_checker.Substitute(local_bindings, + rewrite_constraint.converted_replacement)); // Form a symbolic value naming the non-rewritten associated constant. // The impl constraint will always already exist. @@ -1543,10 +1582,15 @@ class TypeChecker::ConstraintTypeBuilder { if (add_lookup_contexts) { // Add the constraint `.(I.C) = V`, tracking the value and type prior // to conversion for use in rewrites. - Nonnull value = type_checker.Substitute( - local_bindings, rewrite_constraint.unconverted_replacement); - Nonnull type = type_checker.Substitute( - local_bindings, rewrite_constraint.unconverted_replacement_type); + CARBON_ASSIGN_OR_RETURN( + Nonnull value, + type_checker.Substitute( + local_bindings, rewrite_constraint.unconverted_replacement)); + CARBON_ASSIGN_OR_RETURN( + Nonnull type, + type_checker.Substitute( + local_bindings, + rewrite_constraint.unconverted_replacement_type)); AddRewriteConstraint({.constant = constant_value, .unconverted_replacement = value, .unconverted_replacement_type = type, @@ -1564,7 +1608,10 @@ class TypeChecker::ConstraintTypeBuilder { if (std::find_if(values.begin(), values.end(), [&](const Value* v) { return ValueEqual(v, value, std::nullopt); }) == values.end()) { - values.push_back(type_checker.Substitute(local_bindings, value)); + CARBON_ASSIGN_OR_RETURN( + Nonnull subst_value, + type_checker.Substitute(local_bindings, value)); + values.push_back(subst_value); } } AddEqualityConstraint({.values = std::move(values)}); @@ -1572,25 +1619,31 @@ class TypeChecker::ConstraintTypeBuilder { for (const auto& intrinsic_constraint : constraint->intrinsic_constraints()) { + CARBON_ASSIGN_OR_RETURN( + Nonnull type, + type_checker.Substitute(local_bindings, intrinsic_constraint.type)); IntrinsicConstraint converted = { - .type = type_checker.Substitute(local_bindings, - intrinsic_constraint.type), - .kind = intrinsic_constraint.kind, - .arguments = {}}; + .type = type, .kind = intrinsic_constraint.kind, .arguments = {}}; converted.arguments.reserve(intrinsic_constraint.arguments.size()); for (Nonnull argument : intrinsic_constraint.arguments) { - converted.arguments.push_back( + CARBON_ASSIGN_OR_RETURN( + Nonnull subst_arg, type_checker.Substitute(local_bindings, argument)); + converted.arguments.push_back(subst_arg); } AddIntrinsicConstraint(std::move(converted)); } if (add_lookup_contexts) { for (const auto& lookup_context : constraint->lookup_contexts()) { - AddLookupContext({.context = type_checker.Substitute( - local_bindings, lookup_context.context)}); + CARBON_ASSIGN_OR_RETURN( + Nonnull subst_context, + type_checker.Substitute(local_bindings, lookup_context.context)); + AddLookupContext({.context = subst_context}); } } + + return Success(); } class ConstraintsInScopeTracker { @@ -1639,7 +1692,7 @@ class TypeChecker::ConstraintTypeBuilder { const ImplScope& /*impl_scope*/) -> ErrorOr { CARBON_RETURN_IF_ERROR(DeduplicateRewrites(source_loc)); CARBON_RETURN_IF_ERROR(ApplyRewritesToRewrites(type_checker, source_loc)); - ApplyRewritesToConstraints(type_checker); + CARBON_RETURN_IF_ERROR(ApplyRewritesToConstraints(type_checker)); return Success(); } @@ -1755,8 +1808,9 @@ class TypeChecker::ConstraintTypeBuilder { // attempted to reference itself recursively. RewriteInfo info = {.rewrite = rewrite}; current_rewrite_info_ = &info; - Nonnull rebuilt = - type_checker.RebuildValue(rewrite->converted_replacement); + CARBON_ASSIGN_OR_RETURN( + Nonnull rebuilt, + type_checker.RebuildValue(rewrite->converted_replacement)); current_rewrite_info_ = std::nullopt; if (info.rewrite_references_itself) { @@ -1788,7 +1842,8 @@ class TypeChecker::ConstraintTypeBuilder { } // Apply the rewrite constraints throughout our constraints. - auto ApplyRewritesToConstraints(TypeChecker& type_checker) -> void { + auto ApplyRewritesToConstraints(TypeChecker& type_checker) + -> ErrorOr { // Add this builder to the type checker's scope so that it considers our // rewrites. type_checker.partial_constraint_types_.push_back(this); @@ -1798,40 +1853,49 @@ class TypeChecker::ConstraintTypeBuilder { // Apply rewrites through the rewrite constraints. We assume that the // converted replacements have already been rewritten fully. for (auto& rewrite : rewrite_constraints_) { - rewrite.unconverted_replacement = - type_checker.RebuildValue(rewrite.unconverted_replacement); - rewrite.unconverted_replacement_type = - type_checker.RebuildValue(rewrite.unconverted_replacement_type); + CARBON_ASSIGN_OR_RETURN( + rewrite.unconverted_replacement, + type_checker.RebuildValue(rewrite.unconverted_replacement)); + CARBON_ASSIGN_OR_RETURN( + rewrite.unconverted_replacement_type, + type_checker.RebuildValue(rewrite.unconverted_replacement_type)); } // Apply rewrites throughout impl constraints. for (auto& impl_constraint : impl_constraints_) { - impl_constraint.type = type_checker.RebuildValue(impl_constraint.type); - impl_constraint.interface = cast( + CARBON_ASSIGN_OR_RETURN(impl_constraint.type, + type_checker.RebuildValue(impl_constraint.type)); + CARBON_ASSIGN_OR_RETURN( + const auto* subst_interface, type_checker.RebuildValue(impl_constraint.interface)); + impl_constraint.interface = cast(subst_interface); } // Apply rewrites throughout intrinsic constraints. for (auto& intrinsic_constraint : intrinsic_constraints_) { - intrinsic_constraint.type = - type_checker.RebuildValue(intrinsic_constraint.type); + CARBON_ASSIGN_OR_RETURN( + intrinsic_constraint.type, + type_checker.RebuildValue(intrinsic_constraint.type)); for (auto& argument : intrinsic_constraint.arguments) { - argument = type_checker.RebuildValue(argument); + CARBON_ASSIGN_OR_RETURN(argument, type_checker.RebuildValue(argument)); } } // Apply rewrites throughout equality constraints. for (auto& equality_constraint : equality_constraints_) { for (auto*& value : equality_constraint.values) { - value = type_checker.RebuildValue(value); + CARBON_ASSIGN_OR_RETURN(value, type_checker.RebuildValue(value)); } } // Apply rewrites throughout lookup contexts. for (auto& lookup_context : lookup_contexts_) { - lookup_context.context = - type_checker.RebuildValue(lookup_context.context); + CARBON_ASSIGN_OR_RETURN( + lookup_context.context, + type_checker.RebuildValue(lookup_context.context)); } + + return Success(); } Nonnull arena_; @@ -1874,9 +1938,10 @@ class TypeChecker::SubstitutedGenericBindings { // Substitutes into a generic binding and adds it to the bindings map. auto SubstituteIntoGenericBinding(Nonnull old_binding) - -> Nonnull { - Nonnull new_type = - type_checker_->Substitute(bindings_, &old_binding->static_type()); + -> ErrorOr> { + CARBON_ASSIGN_OR_RETURN( + Nonnull new_type, + type_checker_->Substitute(bindings_, &old_binding->static_type())); Nonnull new_binding = type_checker_->arena_->New( old_binding->source_loc(), old_binding->name(), @@ -1906,14 +1971,14 @@ class TypeChecker::SubstitutedGenericBindings { auto TypeChecker::Substitute(const Bindings& bindings, Nonnull type) const - -> Nonnull { + -> ErrorOr> { // Don't waste time recursively rebuilding a type if we have nothing to // substitute. if (bindings.empty()) { return type; } - const auto* result = SubstituteImpl(bindings, type); + CARBON_ASSIGN_OR_RETURN(const auto* result, SubstituteImpl(bindings, type)); if (trace_stream_->is_enabled()) { *trace_stream_ << "substitution of {"; @@ -1930,7 +1995,7 @@ auto TypeChecker::Substitute(const Bindings& bindings, } auto TypeChecker::RebuildValue(Nonnull value) const - -> Nonnull { + -> ErrorOr> { return SubstituteImpl(Bindings(), value); } @@ -1979,19 +2044,28 @@ class TypeChecker::SubstituteTransform // For an associated constant, look for a rewrite. auto operator()(Nonnull assoc) - -> Nonnull { - Nonnull base = Transform(&assoc->base()); - Nonnull interface = Transform(&assoc->interface()); + -> ErrorOr> { + CARBON_ASSIGN_OR_RETURN(Nonnull base, + Transform(&assoc->base())); + CARBON_ASSIGN_OR_RETURN(Nonnull interface, + Transform(&assoc->interface())); // If we're substituting into an associated constant, we may now be able // to rewrite it to a concrete value. - if (auto rewritten_value = type_checker_->LookupRewriteInTypeOf( - base, interface, &assoc->constant())) { + CARBON_ASSIGN_OR_RETURN(auto rewritten_value, + type_checker_->LookupRewriteInTypeOf( + base, interface, &assoc->constant())); + if (rewritten_value) { return (*rewritten_value)->converted_replacement; } - const auto* witness = cast(Transform(&assoc->witness())); - witness = type_checker_->RefineWitness(witness, base, interface); - if (auto rewritten_value = type_checker_->LookupRewriteInWitness( - witness, interface, &assoc->constant())) { + CARBON_ASSIGN_OR_RETURN(Nonnull witness_value, + Transform(&assoc->witness())); + const auto* witness = cast(witness_value); + CARBON_ASSIGN_OR_RETURN( + witness, type_checker_->RefineWitness(witness, base, interface)); + CARBON_ASSIGN_OR_RETURN(rewritten_value, + type_checker_->LookupRewriteInWitness( + witness, interface, &assoc->constant())); + if (rewritten_value) { return (*rewritten_value)->converted_replacement; } return type_checker_->arena_->New( @@ -2002,29 +2076,34 @@ class TypeChecker::SubstituteTransform // TODO: This is probably not specific to substitution, and would apply to // other transforms too. auto operator()(Nonnull fn_type) - -> Nonnull { + -> ErrorOr> { SubstitutedGenericBindings subst_bindings(type_checker_, bindings_); // Apply substitution to into generic parameters and deduced bindings. std::vector generic_parameters; for (const FunctionType::GenericParameter& gp : fn_type->generic_parameters()) { + CARBON_ASSIGN_OR_RETURN( + Nonnull subst_binding, + subst_bindings.SubstituteIntoGenericBinding(gp.binding)); generic_parameters.push_back( - {.index = gp.index, - .binding = subst_bindings.SubstituteIntoGenericBinding(gp.binding)}); + {.index = gp.index, .binding = subst_binding}); } std::vector> deduced_bindings; for (Nonnull gb : fn_type->deduced_bindings()) { - deduced_bindings.push_back( - subst_bindings.SubstituteIntoGenericBinding(gb)); + CARBON_ASSIGN_OR_RETURN(Nonnull subst_binding, + subst_bindings.SubstituteIntoGenericBinding(gb)); + deduced_bindings.push_back(subst_binding); } // Apply substitution to parameter and return types and create the new // function type. - const auto* param = type_checker_->SubstituteImpl(subst_bindings.bindings(), - &fn_type->parameters()); - const auto* ret = type_checker_->SubstituteImpl(subst_bindings.bindings(), - &fn_type->return_type()); + CARBON_ASSIGN_OR_RETURN(const auto* param, type_checker_->SubstituteImpl( + subst_bindings.bindings(), + &fn_type->parameters())); + CARBON_ASSIGN_OR_RETURN(const auto* ret, type_checker_->SubstituteImpl( + subst_bindings.bindings(), + &fn_type->return_type())); return type_checker_->arena_->New( param, std::move(generic_parameters), ret, std::move(deduced_bindings), std::move(subst_bindings).TakeImplBindings()); @@ -2033,7 +2112,7 @@ class TypeChecker::SubstituteTransform // Substituting into a `ConstraintType` needs special handling if we replace // its self type. auto operator()(Nonnull constraint) - -> Nonnull { + -> ErrorOr> { if (auto it = bindings_.args().find(constraint->self_binding()); it != bindings_.args().end()) { // This happens when we substitute into the parameter type of a @@ -2044,7 +2123,9 @@ class TypeChecker::SubstituteTransform type_of_type = &var_type->binding().static_type(); } else if (const auto* assoc_type = dyn_cast(it->second)) { - type_of_type = type_checker_->GetTypeForAssociatedConstant(assoc_type); + CARBON_ASSIGN_OR_RETURN( + type_of_type, + type_checker_->GetTypeForAssociatedConstant(assoc_type)); } else { type_of_type = type_checker_->arena_->New(); } @@ -2060,9 +2141,10 @@ class TypeChecker::SubstituteTransform } ConstraintTypeBuilder builder(type_checker_->arena_, constraint->self_binding()->source_loc()); - builder.AddAndSubstitute(*type_checker_, constraint, builder.GetSelfType(), - builder.GetSelfWitness(), bindings_, - /*add_lookup_contexts=*/true); + CARBON_RETURN_IF_ERROR(builder.AddAndSubstitute( + *type_checker_, constraint, builder.GetSelfType(), + builder.GetSelfWitness(), bindings_, + /*add_lookup_contexts=*/true)); Nonnull new_constraint = std::move(builder).Build(); if (const auto* trace_stream = type_checker_->trace_stream_; trace_stream->is_enabled()) { @@ -2079,14 +2161,14 @@ class TypeChecker::SubstituteTransform auto TypeChecker::SubstituteImpl(const Bindings& bindings, Nonnull type) const - -> Nonnull { + -> ErrorOr> { return SubstituteTransform(this, bindings).Transform(type); } auto TypeChecker::RefineWitness(Nonnull witness, Nonnull type, Nonnull constraint) const - -> Nonnull { + -> ErrorOr> { if (!top_level_impl_scope_) { return witness; } @@ -2103,21 +2185,21 @@ auto TypeChecker::RefineWitness(Nonnull witness, } // Attempt to look for an impl witness in the top-level impl scope. - // TODO: This can fail due to non-terminating impl matching. That should - // result in a hard error. - if (auto refined_witness = - (*top_level_impl_scope_) - ->Resolve(constraint, type, SourceLocation::DiagnosticsIgnored(), - *this); - refined_witness.ok()) { + // TODO: Provide a location. + CARBON_ASSIGN_OR_RETURN( + std::optional> refined_witness, + (*top_level_impl_scope_) + ->TryResolve(constraint, type, SourceLocation::DiagnosticsIgnored(), + *this, /*bindings=*/{}, + /*diagnose_missing_impl=*/false)); + if (refined_witness) { return *refined_witness; - } else { - if (trace_stream_->is_enabled()) { - *trace_stream_ << "could not refine " << *witness << ": " - << refined_witness.error().message() << "\n"; - } - return witness; } + + if (trace_stream_->is_enabled()) { + *trace_stream_ << "could not refine " << *witness << "\n"; + } + return witness; } auto TypeChecker::MatchImpl(const InterfaceType& iface, @@ -2183,7 +2265,10 @@ auto TypeChecker::MatchImpl(const InterfaceType& iface, *trace_stream_ << "matched with " << *impl.type << " as " << *impl.interface << "\n\n"; } - return {cast(Substitute(*bindings_or_error, impl.witness))}; + CARBON_ASSIGN_OR_RETURN( + const auto* subst_witness, + SubstituteCast(*bindings_or_error, impl.witness)); + return {subst_witness}; } } @@ -2209,15 +2294,15 @@ auto TypeChecker::ConvertToConstraintType( if (const auto* iface_type = dyn_cast(constraint)) { CARBON_RETURN_IF_ERROR( ExpectCompleteType(source_loc, "constraint", iface_type)); - return cast(Substitute( - iface_type->bindings(), *iface_type->declaration().constraint_type())); + return SubstituteCast( + iface_type->bindings(), *iface_type->declaration().constraint_type()); } if (const auto* constraint_type = dyn_cast(constraint)) { CARBON_RETURN_IF_ERROR( ExpectCompleteType(source_loc, "constraint", constraint_type)); - return cast( - Substitute(constraint_type->bindings(), - *constraint_type->declaration().constraint_type())); + return SubstituteCast( + constraint_type->bindings(), + *constraint_type->declaration().constraint_type()); } if (isa(constraint)) { // TODO: Should we build this once and cache it? @@ -2235,9 +2320,10 @@ auto TypeChecker::CombineConstraints( -> ErrorOr> { ConstraintTypeBuilder builder(arena_, source_loc); for (Nonnull constraint : constraints) { - builder.AddAndSubstitute(*this, constraint, builder.GetSelfType(), - builder.GetSelfWitness(), Bindings(), - /*add_lookup_contexts=*/true); + CARBON_RETURN_IF_ERROR( + builder.AddAndSubstitute(*this, constraint, builder.GetSelfType(), + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/true)); } return std::move(builder).Build(); } @@ -2288,7 +2374,8 @@ auto TypeChecker::DeduceCallBindings( call.set_bindings(std::move(*bindings)); // Convert the arguments to the deduced and substituted parameter type. - Nonnull param_type = Substitute(call.bindings(), params_type); + CARBON_ASSIGN_OR_RETURN(Nonnull param_type, + Substitute(call.bindings(), params_type)); CARBON_ASSIGN_OR_RETURN( Nonnull converted_argument, ImplicitlyConvert("call", impl_scope, &call.argument(), param_type)); @@ -2347,7 +2434,8 @@ auto TypeChecker::LookupInConstraint(SourceLocation source_loc, } auto TypeChecker::GetTypeForAssociatedConstant( - Nonnull assoc) const -> Nonnull { + Nonnull assoc) const + -> ErrorOr> { const auto* assoc_type = &assoc->constant().static_type(); Bindings bindings = assoc->interface().bindings(); bindings.Add(assoc->interface().declaration().self(), &assoc->base(), @@ -2358,7 +2446,7 @@ auto TypeChecker::GetTypeForAssociatedConstant( auto TypeChecker::LookupRewriteInTypeOf( Nonnull type, Nonnull interface, Nonnull member) const - -> std::optional { + -> ErrorOr> { // If the type is the self type of an incomplete `where` expression or a // constraint type we're in the process of resolving, find its set of // rewrites. These rewrites may not be complete -- earlier rewrites will have @@ -2375,7 +2463,7 @@ auto TypeChecker::LookupRewriteInTypeOf( current_rewrite_info && (*current_rewrite_info)->rewrite == *result) { (*current_rewrite_info)->rewrite_references_itself = true; - return std::nullopt; + return {std::nullopt}; } return result; } @@ -2389,7 +2477,7 @@ auto TypeChecker::LookupRewriteInTypeOf( // binding. This happens when forming the type of a generic binding. Just // say there are no rewrites yet; any rewrites will be applied when the // constraint on the binding's type is resolved. - return std::nullopt; + return {std::nullopt}; } return LookupRewrite(&var_type->binding().static_type(), interface, member); } @@ -2403,7 +2491,7 @@ auto TypeChecker::LookupRewriteInTypeOf( // associated constant, if `.Self` is used to access an associated // constant. Just say that there are not rewrites yet; any rewrites will // be applied when the constraint on the binding's type is resolved. - return std::nullopt; + return {std::nullopt}; } // The following is an expanded version of // return LookupRewrite(GetTypeForAssociatedConstant(assoc_const), @@ -2419,39 +2507,47 @@ auto TypeChecker::LookupRewriteInTypeOf( Bindings bindings = assoc_const->interface().bindings(); bindings.Add(assoc_const->interface().declaration().self(), &assoc_const->base(), &assoc_const->witness()); - if (ValueEqual(interface, - Substitute(bindings, &rewrite.constant->interface()), - std::nullopt)) { - // TODO: These substitutions can lead to infinite recursion. + // TODO: These substitutions can lead to infinite recursion. + CARBON_ASSIGN_OR_RETURN( + Nonnull rewrite_interface, + Substitute(bindings, &rewrite.constant->interface())); + if (ValueEqual(interface, rewrite_interface, std::nullopt)) { + CARBON_ASSIGN_OR_RETURN( + Nonnull unconverted_replacement, + Substitute(bindings, rewrite.unconverted_replacement)); + CARBON_ASSIGN_OR_RETURN( + Nonnull unconverted_replacement_type, + Substitute(bindings, rewrite.unconverted_replacement_type)); + CARBON_ASSIGN_OR_RETURN( + Nonnull converted_replacement, + Substitute(bindings, rewrite.converted_replacement)); RewriteConstraint substituted = { // Not substituted, but our callers don't need it. .constant = rewrite.constant, - .unconverted_replacement = - Substitute(bindings, rewrite.unconverted_replacement), - .unconverted_replacement_type = - Substitute(bindings, rewrite.unconverted_replacement_type), - .converted_replacement = - Substitute(bindings, rewrite.converted_replacement)}; - return arena_->New(substituted); + .unconverted_replacement = unconverted_replacement, + .unconverted_replacement_type = unconverted_replacement_type, + .converted_replacement = converted_replacement}; + return {arena_->New(substituted)}; } } } } - return std::nullopt; + return {std::nullopt}; } auto TypeChecker::LookupRewriteInWitness( Nonnull witness, Nonnull interface, Nonnull member) const - -> std::optional { + -> ErrorOr> { if (const auto* impl_witness = dyn_cast(witness)) { - Nonnull constraint = + CARBON_ASSIGN_OR_RETURN( + Nonnull constraint, Substitute(impl_witness->bindings(), - impl_witness->declaration().constraint_type()); + impl_witness->declaration().constraint_type())); return LookupRewrite(constraint, interface, member); } - return std::nullopt; + return {std::nullopt}; } // Rewrites a member access expression to produce the given constant value. @@ -2493,8 +2589,9 @@ auto TypeChecker::CheckAddrMeAccess( if (func_decl->is_method() && func_decl->self_pattern().kind() == PatternKind::AddrPattern) { access->set_is_addr_me_method(); - Nonnull me_type = - Substitute(bindings, &func_decl->self_pattern().static_type()); + CARBON_ASSIGN_OR_RETURN( + Nonnull me_type, + Substitute(bindings, &func_decl->self_pattern().static_type())); CARBON_RETURN_IF_ERROR( ExpectExactType(access->source_loc(), "method access, receiver type", me_type, &access->object().static_type(), impl_scope)); @@ -2647,8 +2744,9 @@ auto TypeChecker::TypeCheckExp(Nonnull e, FindMemberWithParents(access.member_name(), &t_class)); if (res.has_value()) { auto [member_type, member, member_t_class] = res.value(); - Nonnull field_type = - Substitute(member_t_class->bindings(), member_type); + CARBON_ASSIGN_OR_RETURN( + Nonnull field_type, + Substitute(member_t_class->bindings(), member_type)); access.set_member(arena_->New(member)); access.set_static_type(field_type); access.set_is_type_access(!IsInstanceMember(&access.member())); @@ -2687,8 +2785,9 @@ auto TypeChecker::TypeCheckExp(Nonnull e, if (const auto* var_type = dyn_cast(&object_type)) { constraint = &var_type->binding().static_type(); } else { - constraint = GetTypeForAssociatedConstant( - cast(&object_type)); + CARBON_ASSIGN_OR_RETURN( + constraint, GetTypeForAssociatedConstant( + cast(&object_type))); } CARBON_ASSIGN_OR_RETURN( ConstraintLookupResult result, @@ -2720,8 +2819,8 @@ auto TypeChecker::TypeCheckExp(Nonnull e, witness); const Value& member_type = result.member->static_type(); - Nonnull inst_member_type = - Substitute(bindings, &member_type); + CARBON_ASSIGN_OR_RETURN(Nonnull inst_member_type, + Substitute(bindings, &member_type)) access.set_member(arena_->New(result.member)); access.set_found_in_interface(result.interface); access.set_is_type_access(!IsInstanceMember(&access.member())); @@ -2795,8 +2894,8 @@ auto TypeChecker::TypeCheckExp(Nonnull e, const Value& member_type = result.member->static_type(); Bindings bindings = result.interface->bindings(); bindings.Add(result.interface->declaration().self(), type, witness); - Nonnull inst_member_type = - Substitute(bindings, &member_type); + CARBON_ASSIGN_OR_RETURN(Nonnull inst_member_type, + Substitute(bindings, &member_type)); access.set_static_type(inst_member_type); access.set_value_category(ValueCategory::Let); } @@ -2846,8 +2945,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, return Success(); } - Nonnull parameter_type = Substitute( - choice.bindings(), *(*signature)->parameters_static_type()); + CARBON_ASSIGN_OR_RETURN( + Nonnull parameter_type, + Substitute(choice.bindings(), + *(*signature)->parameters_static_type())); Nonnull type = arena_->New(parameter_type, &choice); // TODO: Should there be a Declaration corresponding to each @@ -2875,8 +2976,9 @@ auto TypeChecker::TypeCheckExp(Nonnull e, if (func.is_method()) { break; } - Nonnull field_type = Substitute( - class_type.bindings(), &member->static_type()); + CARBON_ASSIGN_OR_RETURN(Nonnull field_type, + Substitute(class_type.bindings(), + &member->static_type())); access.set_static_type(field_type); access.set_value_category(ValueCategory::Let); return Success(); @@ -2971,8 +3073,11 @@ auto TypeChecker::TypeCheckExp(Nonnull e, member_name.interface()) { // If we're naming an associated constant, we might have a rewrite for // it that we can apply immediately. - if (auto replacement = LookupRewriteInTypeOf( - *base_type, *iface, *member_name.member().declaration())) { + CARBON_ASSIGN_OR_RETURN( + auto replacement, + LookupRewriteInTypeOf(*base_type, *iface, + *member_name.member().declaration())); + if (replacement) { RewriteMemberAccess(&access, *replacement); return Success(); } @@ -2991,8 +3096,11 @@ auto TypeChecker::TypeCheckExp(Nonnull e, CARBON_ASSIGN_OR_RETURN( Nonnull impl, impl_scope.Resolve(*iface, *base_type, e->source_loc(), *this)); - if (auto replacement = LookupRewriteInWitness( - impl, *iface, *member_name.member().declaration())) { + CARBON_ASSIGN_OR_RETURN( + replacement, + LookupRewriteInWitness(impl, *iface, + *member_name.member().declaration())); + if (replacement) { RewriteMemberAccess(&access, *replacement); return Success(); } @@ -3013,9 +3121,12 @@ auto TypeChecker::TypeCheckExp(Nonnull e, return Bindings(); }; - auto substitute_into_member_type = [&]() { + auto set_static_type_as_member_type = [&]() -> ErrorOr { Nonnull member_type = &member_name.member().type(); - return Substitute(bindings_for_member(), member_type); + CARBON_ASSIGN_OR_RETURN(member_type, + Substitute(bindings_for_member(), member_type)); + access.set_static_type(member_type); + return Success(); }; switch (std::optional> decl = @@ -3024,7 +3135,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, : DeclarationKind::VariableDeclaration) { case DeclarationKind::VariableDeclaration: if (has_instance) { - access.set_static_type(substitute_into_member_type()); + CARBON_RETURN_IF_ERROR(set_static_type_as_member_type()); access.set_value_category(access.object().value_category()); return Success(); } @@ -3036,7 +3147,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, CARBON_CHECK(!has_instance || is_instance_member || !member_name.base_type().has_value()) << "vacuous compound member access"; - access.set_static_type(substitute_into_member_type()); + CARBON_RETURN_IF_ERROR(set_static_type_as_member_type()); access.set_value_category(ValueCategory::Let); CARBON_RETURN_IF_ERROR( CheckAddrMeAccess(&access, cast(*decl), @@ -3046,7 +3157,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, break; } case DeclarationKind::AssociatedConstantDeclaration: - access.set_static_type(substitute_into_member_type()); + CARBON_RETURN_IF_ERROR(set_static_type_as_member_type()); access.set_value_category(access.object().value_category()); return Success(); default: @@ -3326,8 +3437,9 @@ auto TypeChecker::TypeCheckExp(Nonnull e, // Substitute into the return type to determine the type of the call // expression. - Nonnull return_type = - Substitute(call.bindings(), &fun_t.return_type()); + CARBON_ASSIGN_OR_RETURN( + Nonnull return_type, + Substitute(call.bindings(), &fun_t.return_type())); call.set_static_type(return_type); call.set_value_category(ValueCategory::Let); return Success(); @@ -3668,9 +3780,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, base_type)); // Start with the given constraint. - builder.AddAndSubstitute(*this, base, builder.GetSelfType(), - builder.GetSelfWitness(), Bindings(), - /*add_lookup_contexts=*/true); + CARBON_RETURN_IF_ERROR( + builder.AddAndSubstitute(*this, base, builder.GetSelfType(), + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/true)); // Type-check and apply the `where` clauses. for (Nonnull clause : where.clauses()) { @@ -3697,9 +3810,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, "expression after `is`", constraint)); // Transform `where .B is (C where .D is E)` into `where .B is C // and .B.D is E` then add all the resulting constraints. - builder.AddAndSubstitute(*this, constraint_type, type, - builder.GetSelfWitness(), Bindings(), - /*add_lookup_contexts=*/false); + CARBON_RETURN_IF_ERROR( + builder.AddAndSubstitute(*this, constraint_type, type, + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/false)); break; } case WhereClauseKind::EqualsWhereClause: { @@ -3757,11 +3871,13 @@ auto TypeChecker::TypeCheckExp(Nonnull e, // Convert the replacement value to the type of the associated // constant and find the converted value. This is the value that // we'll produce during evaluation and substitution. + CARBON_ASSIGN_OR_RETURN( + Nonnull constraint_type, + GetTypeForAssociatedConstant(constant_value)); CARBON_ASSIGN_OR_RETURN( Nonnull converted_expression, - ImplicitlyConvert( - "rewrite constraint", inner_impl_scope, replacement_literal, - GetTypeForAssociatedConstant(constant_value))); + ImplicitlyConvert("rewrite constraint", inner_impl_scope, + replacement_literal, constraint_type)); CARBON_ASSIGN_OR_RETURN( Nonnull converted_value, InterpExp(converted_expression, arena_, trace_stream_)); @@ -3912,10 +4028,15 @@ auto TypeChecker::TypeCheckWhereClause(Nonnull clause, // conversion. Nonnull lhs_type = &equals_clause.lhs().static_type(); Nonnull rhs_type = &equals_clause.rhs().static_type(); - if (!IsImplicitlyConvertible(lhs_type, rhs_type, impl_scope, - /*allow_user_defined_conversions=*/false) && - !IsImplicitlyConvertible(rhs_type, lhs_type, impl_scope, - /*allow_user_defined_conversions=*/false)) { + CARBON_ASSIGN_OR_RETURN( + bool lhs_converts_to_rhs, + IsImplicitlyConvertible(lhs_type, rhs_type, impl_scope, + /*allow_user_defined_conversions=*/false)); + CARBON_ASSIGN_OR_RETURN( + bool rhs_converts_to_lhs, + IsImplicitlyConvertible(rhs_type, lhs_type, impl_scope, + /*allow_user_defined_conversions=*/false)); + if (!lhs_converts_to_rhs && !rhs_converts_to_lhs) { return ProgramError(clause->source_loc()) << "type mismatch between values in `where LHS == RHS`\n" << " LHS type: " << *lhs_type << "\n" @@ -4077,8 +4198,10 @@ auto TypeChecker::TypeCheckPattern( << "` does not expect an argument list"; } - Nonnull parameter_type = Substitute( - choice_type.bindings(), *(*signature)->parameters_static_type()); + CARBON_ASSIGN_OR_RETURN( + Nonnull parameter_type, + Substitute(choice_type.bindings(), + *(*signature)->parameters_static_type())); CARBON_RETURN_IF_ERROR(TypeCheckPattern(&alternative.arguments(), parameter_type, impl_scope, enclosing_value_category)); @@ -4165,8 +4288,9 @@ auto TypeChecker::TypeCheckGenericBinding(GenericBinding& binding, // resolved type of the binding. Eg, `T:! X where .Self is Y` resolves // to `T:! `. ConstraintTypeBuilder builder(arena_, &binding, impl_binding); - builder.AddAndSubstitute(*this, constraint, symbolic_value, witness, - Bindings(), /*add_lookup_contexts=*/true); + CARBON_RETURN_IF_ERROR( + builder.AddAndSubstitute(*this, constraint, symbolic_value, witness, + Bindings(), /*add_lookup_contexts=*/true)); if (trace_stream_->is_enabled()) { *trace_stream_ << "resolving constraint type for " << binding << " from " << *constraint << "\n"; @@ -5085,9 +5209,10 @@ auto TypeChecker::DeclareConstraintTypeDeclaration( Nonnull constraint_type, ConvertToConstraintType(m->source_loc(), "extends declaration", base)); - builder.AddAndSubstitute(*this, constraint_type, builder.GetSelfType(), - builder.GetSelfWitness(), Bindings(), - /*add_lookup_contexts=*/true); + CARBON_RETURN_IF_ERROR(builder.AddAndSubstitute( + *this, constraint_type, builder.GetSelfType(), + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/true)); break; } @@ -5104,9 +5229,10 @@ auto TypeChecker::DeclareConstraintTypeDeclaration( Nonnull constraint_type, ConvertToConstraintType(m->source_loc(), "impl as declaration", constraint)); - builder.AddAndSubstitute(*this, constraint_type, impl_type, - builder.GetSelfWitness(), Bindings(), - /*add_lookup_contexts=*/false); + CARBON_RETURN_IF_ERROR( + builder.AddAndSubstitute(*this, constraint_type, impl_type, + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/false)); break; } @@ -5139,9 +5265,10 @@ auto TypeChecker::DeclareConstraintTypeDeclaration( // of the constraint type can rely on. if (const auto* constraint_type = dyn_cast(constraint)) { - builder.AddAndSubstitute(*this, constraint_type, assoc_value, - builder.GetSelfWitness(), Bindings(), - /*add_lookup_contexts=*/false); + CARBON_RETURN_IF_ERROR( + builder.AddAndSubstitute(*this, constraint_type, assoc_value, + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/false)); } break; } @@ -5257,8 +5384,8 @@ auto TypeChecker::CheckImplIsComplete(Nonnull iface_type, Bindings bindings = iface_type->bindings(); bindings.Add(iface_decl.self(), self_type, iface_witness); - Nonnull iface_mem_type = - Substitute(bindings, &m->static_type()); + CARBON_ASSIGN_OR_RETURN(Nonnull iface_mem_type, + Substitute(bindings, &m->static_type())); // TODO: How should the signature in the implementation be permitted // to differ from the signature in the interface? CARBON_RETURN_IF_ERROR( @@ -5400,9 +5527,10 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull impl_decl, self_binding->set_impl_binding(impl_binding); ConstraintTypeBuilder builder(arena_, self_binding, impl_binding); - builder.AddAndSubstitute(*this, implemented_constraint, impl_type_value, - builder.GetSelfWitness(), Bindings(), - /*add_lookup_contexts=*/true); + CARBON_RETURN_IF_ERROR( + builder.AddAndSubstitute(*this, implemented_constraint, impl_type_value, + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/true)); if (trace_stream_->is_enabled()) { *trace_stream_ << "resolving impl constraint type for " << *impl_decl << " from " << *implemented_constraint << "\n"; @@ -5921,8 +6049,8 @@ auto TypeChecker::FindMixedMemberAndType( // TODO: What is the type of Self? Do we ever need a witness? temp_map.Add(mixin->declaration().self(), enclosing_type, std::nullopt); - const auto* const mix_member_type = - Substitute(temp_map, res.value().first); + CARBON_ASSIGN_OR_RETURN(const auto* const mix_member_type, + Substitute(temp_map, res.value().first)); return {std::make_pair(mix_member_type, res.value().second)}; } else { return res; diff --git a/explorer/interpreter/type_checker.h b/explorer/interpreter/type_checker.h index 7e58d26741bf..02d3921c8001 100644 --- a/explorer/interpreter/type_checker.h +++ b/explorer/interpreter/type_checker.h @@ -12,6 +12,7 @@ #include #include +#include "common/error.h" #include "common/ostream.h" #include "explorer/ast/ast.h" #include "explorer/ast/expression.h" @@ -24,6 +25,7 @@ #include "explorer/interpreter/interpreter.h" #include "explorer/interpreter/matching_impl_set.h" #include "explorer/interpreter/trace_stream.h" +#include "llvm/ADT/identity.h" namespace Carbon { @@ -45,11 +47,24 @@ class TypeChecker { // processed. auto TypeCheck(AST& ast) -> ErrorOr; - // Construct a type that is the same as `type` except that occurrences - // of type variables (aka. `GenericBinding` and references to `ImplBinding`) - // are replaced by their corresponding type or witness in `dict`. - auto Substitute(const Bindings& bindings, Nonnull type) const - -> Nonnull; + // Construct a value that is the same as `value` except that occurrences + // of generic parameters (aka. `GenericBinding` and references to + // `ImplBinding`) are replaced by their corresponding value or witness in + // `bindings`. + auto Substitute(const Bindings& bindings, Nonnull value) const + -> ErrorOr>; + + // Same as `Substitute`, but cast the result to the type given as a template + // argument, which must be explicitly specified. The `remove_cv_t` here + // blocks template argument deduction. + template + auto SubstituteCast(const Bindings& bindings, + Nonnull*> value) const + -> ErrorOr> { + CARBON_ASSIGN_OR_RETURN(Nonnull subst_value, + Substitute(bindings, value)); + return llvm::cast(subst_value); + } // Attempts to refine a witness that might be symbolic into an impl witness, // using `impl` declarations that have been declared and type-checked so far. @@ -57,7 +72,7 @@ class TypeChecker { auto RefineWitness(Nonnull witness, Nonnull type, Nonnull constraint) const - -> Nonnull; + -> ErrorOr>; // If `impl` can be an implementation of interface `iface` for the given // `type`, then return the witness for this `impl`. Otherwise return @@ -103,7 +118,7 @@ class TypeChecker { // in the given scope. auto IsIntrinsicConstraintSatisfied(const IntrinsicConstraint& constraint, const ImplScope& impl_scope) const - -> bool; + -> ErrorOr; private: class ConstraintTypeBuilder; @@ -359,11 +374,11 @@ class TypeChecker { // Returns the field names of the class together with their types. auto FieldTypes(const NominalClassType& class_type) const - -> std::vector; + -> ErrorOr>; // Returns the field names and types of the class and its parents. auto FieldTypesWithBase(const NominalClassType& class_type) const - -> std::vector; + -> ErrorOr>; // Returns true if source_fields and destination_fields contain the same set // of names, and each value in source_fields is implicitly convertible to @@ -372,7 +387,7 @@ class TypeChecker { auto FieldTypesImplicitlyConvertible( llvm::ArrayRef source_fields, llvm::ArrayRef destination_fields, - const ImplScope& impl_scope) const -> bool; + const ImplScope& impl_scope) const -> ErrorOr; // Returns true if *source is implicitly convertible to *destination. *source // and *destination must be concrete types. @@ -384,7 +399,7 @@ class TypeChecker { Nonnull destination, const ImplScope& impl_scope, bool allow_user_defined_conversions) const - -> bool; + -> ErrorOr; // Attempt to implicitly convert type-checked expression `source` to the type // `destination`. @@ -421,13 +436,14 @@ class TypeChecker { // Rebuild a value in the current type-checking context. Applies any rewrites // that are in scope and attempts to resolve associated constants using impls // that have been declared since the value was formed. - auto RebuildValue(Nonnull value) const -> Nonnull; + auto RebuildValue(Nonnull value) const + -> ErrorOr>; // Implementation of Substitute and RebuildValue. Does not check that // bindings are nonempty, nor does it trace its progress. auto SubstituteImpl(const Bindings& bindings, Nonnull type) const - -> Nonnull; + -> ErrorOr>; // The name of a builtin interface, with any arguments. struct BuiltinInterfaceName { @@ -468,8 +484,8 @@ class TypeChecker { -> ErrorOr>; // Gets the type for the given associated constant. - auto GetTypeForAssociatedConstant( - Nonnull assoc) const -> Nonnull; + auto GetTypeForAssociatedConstant(Nonnull assoc) + const -> ErrorOr>; // Look up a member name in a constraint, which might be a single interface or // a compound constraint. @@ -484,14 +500,14 @@ class TypeChecker { auto LookupRewriteInTypeOf(Nonnull type, Nonnull interface, Nonnull member) const - -> std::optional; + -> ErrorOr>; // Given a witness value, look for a rewrite for the given associated // constant. auto LookupRewriteInWitness(Nonnull witness, Nonnull interface, Nonnull member) const - -> std::optional; + -> ErrorOr>; // Adds a member of a declaration to collected_members_ auto CollectMember(Nonnull enclosing_decl,