|
|
@@ -506,40 +506,6 @@ static auto FindField(llvm::ArrayRef<NamedValue> fields,
|
|
|
return *it;
|
|
|
}
|
|
|
|
|
|
-auto TypeChecker::FieldTypesImplicitlyConvertible(
|
|
|
- llvm::ArrayRef<NamedValue> source_fields,
|
|
|
- llvm::ArrayRef<NamedValue> destination_fields,
|
|
|
- const ImplScope& impl_scope) const -> ErrorOr<bool> {
|
|
|
- // TODO: If default fields are implemented, the
|
|
|
- // code must be adapted to skip them.
|
|
|
- // Ensure every field name exists in the destination.
|
|
|
- for (const auto& dest_field : destination_fields) {
|
|
|
- if (!FindField(source_fields, dest_field.name)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- for (const auto& source_field : source_fields) {
|
|
|
- std::optional<NamedValue> destination_field =
|
|
|
- FindField(destination_fields, source_field.name);
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
auto TypeChecker::FieldTypes(const NominalClassType& class_type) const
|
|
|
-> ErrorOr<std::vector<NamedValue>> {
|
|
|
std::vector<NamedValue> field_types;
|
|
|
@@ -564,7 +530,7 @@ auto TypeChecker::FieldTypesWithBase(const NominalClassType& class_type) const
|
|
|
-> ErrorOr<std::vector<NamedValue>> {
|
|
|
CARBON_ASSIGN_OR_RETURN(auto fields, FieldTypes(class_type));
|
|
|
if (const auto base_type = class_type.base()) {
|
|
|
- CARBON_ASSIGN_OR_RETURN(auto base_fields,
|
|
|
+ CARBON_ASSIGN_OR_RETURN(std::vector<NamedValue> base_fields,
|
|
|
FieldTypesWithBase(*base_type.value()));
|
|
|
fields.emplace_back(NamedValue{std::string(NominalClassValue::BaseField),
|
|
|
base_type.value()});
|
|
|
@@ -576,32 +542,91 @@ auto TypeChecker::IsImplicitlyConvertible(
|
|
|
Nonnull<const Value*> source, Nonnull<const Value*> destination,
|
|
|
const ImplScope& impl_scope, bool allow_user_defined_conversions) const
|
|
|
-> ErrorOr<bool> {
|
|
|
- // Check for an exact match or for an implicit conversion.
|
|
|
- // TODO: `impl` definitions of `ImplicitAs` should be provided to cover these
|
|
|
- // conversions.
|
|
|
+ // Check for an exact match to avoid impl lookup in this common case.
|
|
|
CARBON_CHECK(IsNonDeduceableType(source));
|
|
|
CARBON_CHECK(IsNonDeduceableType(destination));
|
|
|
if (IsSameType(source, destination, impl_scope)) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ // If the source is a type, or a type-like tuple, then it might convert to
|
|
|
+ // another type-of-type. This can't be done by `ImplicitAs` because it
|
|
|
+ // depends on the value, not only on the type.
|
|
|
+ //
|
|
|
+ // TODO: We can't tell whether the conversion to this type-of-type would
|
|
|
+ // work, because we don't have the source value, only its type. So we allow
|
|
|
+ // this conversion if the source converts to `type`, even if it won't convert
|
|
|
+ // to the actual destination type. We'll catch any problems when we actually
|
|
|
+ // come to perform the conversion.
|
|
|
+ if (isa<TupleType>(source) && IsTypeOfType(destination)) {
|
|
|
+ return IsBuiltinConversion(source, arena_->New<TypeType>(), impl_scope,
|
|
|
+ allow_user_defined_conversions);
|
|
|
+ }
|
|
|
+ if (IsTypeOfType(source) && IsTypeOfType(destination)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we're not supposed to look for a user-defined conversion, check for
|
|
|
+ // builtin conversions, which are normally found by impl lookup.
|
|
|
+ if (!allow_user_defined_conversions) {
|
|
|
+ return IsBuiltinConversion(source, destination, impl_scope,
|
|
|
+ allow_user_defined_conversions);
|
|
|
+ }
|
|
|
+
|
|
|
+ // We didn't find a builtin implicit conversion. Check if a user-defined one
|
|
|
+ // exists.
|
|
|
+ SourceLocation source_loc = SourceLocation::DiagnosticsIgnored();
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ Nonnull<const InterfaceType*> iface_type,
|
|
|
+ GetBuiltinInterfaceType(
|
|
|
+ source_loc, BuiltinInterfaceName{Builtin::ImplicitAs, destination}));
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ std::optional<Nonnull<const Witness*>> conversion_witness,
|
|
|
+ impl_scope.TryResolve(iface_type, source, source_loc, *this,
|
|
|
+ /*bindings=*/{}, /*diagnose_missing_impl=*/false));
|
|
|
+ return conversion_witness.has_value();
|
|
|
+}
|
|
|
+
|
|
|
+auto TypeChecker::IsBuiltinConversion(Nonnull<const Value*> source,
|
|
|
+ Nonnull<const Value*> destination,
|
|
|
+ const ImplScope& impl_scope,
|
|
|
+ bool allow_user_defined_conversions) const
|
|
|
+ -> ErrorOr<bool> {
|
|
|
switch (source->kind()) {
|
|
|
case Value::Kind::StructType:
|
|
|
switch (destination->kind()) {
|
|
|
case Value::Kind::StructType: {
|
|
|
- CARBON_ASSIGN_OR_RETURN(
|
|
|
- bool fields_convertible,
|
|
|
- FieldTypesImplicitlyConvertible(
|
|
|
- cast<StructType>(*source).fields(),
|
|
|
- cast<StructType>(*destination).fields(), impl_scope));
|
|
|
- if (fields_convertible) {
|
|
|
- return true;
|
|
|
+ llvm::ArrayRef<NamedValue> source_fields =
|
|
|
+ cast<StructType>(*source).fields();
|
|
|
+ llvm::ArrayRef<NamedValue> destination_fields =
|
|
|
+ cast<StructType>(*destination).fields();
|
|
|
+ // Ensure every source field exists in the destination type.
|
|
|
+ for (const auto& source_field : source_fields) {
|
|
|
+ if (!FindField(destination_fields, source_field.name)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
- break;
|
|
|
+ // Ensure every destination field is initialized.
|
|
|
+ for (const auto& destination_field : destination_fields) {
|
|
|
+ std::optional<NamedValue> source_field =
|
|
|
+ FindField(source_fields, destination_field.name);
|
|
|
+ if (!source_field.has_value()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ bool convertible,
|
|
|
+ IsImplicitlyConvertible(source_field->value,
|
|
|
+ destination_field.value, impl_scope,
|
|
|
+ allow_user_defined_conversions));
|
|
|
+ if (!convertible) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
}
|
|
|
case Value::Kind::NominalClassType: {
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
- auto field_types,
|
|
|
+ std::vector<NamedValue> field_types,
|
|
|
FieldTypesWithBase(cast<NominalClassType>(*destination)));
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
bool convertible,
|
|
|
@@ -641,7 +666,7 @@ auto TypeChecker::IsImplicitlyConvertible(
|
|
|
bool convertible,
|
|
|
IsImplicitlyConvertible(
|
|
|
source_tuple.elements()[i], destination_tuple.elements()[i],
|
|
|
- impl_scope, /*allow_user_defined_conversions=*/false));
|
|
|
+ impl_scope, allow_user_defined_conversions));
|
|
|
if (!convertible) {
|
|
|
all_ok = false;
|
|
|
break;
|
|
|
@@ -663,7 +688,7 @@ auto TypeChecker::IsImplicitlyConvertible(
|
|
|
bool convertible,
|
|
|
IsImplicitlyConvertible(
|
|
|
source_element, &destination_array.element_type(),
|
|
|
- impl_scope, /*allow_user_defined_conversions=*/false));
|
|
|
+ impl_scope, allow_user_defined_conversions));
|
|
|
if (!convertible) {
|
|
|
all_ok = false;
|
|
|
break;
|
|
|
@@ -674,18 +699,14 @@ auto TypeChecker::IsImplicitlyConvertible(
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
- case Value::Kind::TypeType:
|
|
|
- case Value::Kind::InterfaceType:
|
|
|
- case Value::Kind::NamedConstraintType:
|
|
|
- case Value::Kind::ConstraintType: {
|
|
|
- // A tuple value converts to a type if all of its fields do.
|
|
|
+ case Value::Kind::TypeType: {
|
|
|
+ // A tuple value converts to `type` if all of its fields do.
|
|
|
bool all_types = true;
|
|
|
for (Nonnull<const Value*> source_element : source_tuple.elements()) {
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
bool convertible,
|
|
|
- IsImplicitlyConvertible(
|
|
|
- source_element, destination, impl_scope,
|
|
|
- /*allow_user_defined_conversions=*/false));
|
|
|
+ IsImplicitlyConvertible(source_element, destination, impl_scope,
|
|
|
+ allow_user_defined_conversions));
|
|
|
if (!convertible) {
|
|
|
all_types = false;
|
|
|
break;
|
|
|
@@ -701,14 +722,6 @@ auto TypeChecker::IsImplicitlyConvertible(
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
- case Value::Kind::TypeType:
|
|
|
- case Value::Kind::InterfaceType:
|
|
|
- case Value::Kind::NamedConstraintType:
|
|
|
- case Value::Kind::ConstraintType:
|
|
|
- // TODO: We can't tell whether the conversion to this type-of-type would
|
|
|
- // work, because that depends on the source value, and we only have its
|
|
|
- // type.
|
|
|
- return IsTypeOfType(destination);
|
|
|
case Value::Kind::PointerType: {
|
|
|
if (destination->kind() != Value::Kind::PointerType) {
|
|
|
break;
|
|
|
@@ -729,24 +742,13 @@ auto TypeChecker::IsImplicitlyConvertible(
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- // If we're not supposed to look for a user-defined conversion, we're done.
|
|
|
- if (!allow_user_defined_conversions) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // We didn't find a builtin implicit conversion. Try a user-defined one.
|
|
|
- SourceLocation source_loc = SourceLocation::DiagnosticsIgnored();
|
|
|
- ErrorOr<Nonnull<const InterfaceType*>> iface_type = GetBuiltinInterfaceType(
|
|
|
- source_loc, BuiltinInterfaceName{Builtin::ImplicitAs, destination});
|
|
|
- // TODO: If the Resolve call fails with a hard error, don't swallow it.
|
|
|
- return iface_type.ok() &&
|
|
|
- impl_scope.Resolve(*iface_type, source, source_loc, *this).ok();
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
auto TypeChecker::BuildSubtypeConversion(Nonnull<Expression*> source,
|
|
|
Nonnull<const PointerType*> src_ptr,
|
|
|
Nonnull<const PointerType*> dest_ptr)
|
|
|
- -> ErrorOr<Nonnull<const Expression*>> {
|
|
|
+ -> ErrorOr<Nonnull<Expression*>> {
|
|
|
const auto* src_class = cast<NominalClassType>(&src_ptr->pointee_type());
|
|
|
const auto* dest_class = cast<NominalClassType>(&dest_ptr->pointee_type());
|
|
|
const auto dest = dest_class->declaration().name();
|
|
|
@@ -766,6 +768,186 @@ auto TypeChecker::BuildSubtypeConversion(Nonnull<Expression*> source,
|
|
|
return last_expr;
|
|
|
}
|
|
|
|
|
|
+auto TypeChecker::BuildBuiltinConversion(Nonnull<Expression*> source,
|
|
|
+ Nonnull<const Value*> destination,
|
|
|
+ const ImplScope& impl_scope)
|
|
|
+ -> ErrorOr<Nonnull<Expression*>> {
|
|
|
+ Nonnull<const Value*> source_type = &source->static_type();
|
|
|
+
|
|
|
+ if (trace_stream_->is_enabled()) {
|
|
|
+ *trace_stream_ << "building builtin conversion from " << *source_type
|
|
|
+ << " to " << *destination << "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Build a simple conversion that the interpreter can perform directly.
|
|
|
+ auto make_builtin_conversion = [&](Nonnull<Expression*> from) {
|
|
|
+ auto* result = arena_->New<BuiltinConvertExpression>(from);
|
|
|
+ result->set_static_type(destination);
|
|
|
+ result->set_expression_category(ExpressionCategory::Value);
|
|
|
+ return result;
|
|
|
+ };
|
|
|
+
|
|
|
+ // Report that the conversion was not possible. This error should only be
|
|
|
+ // visible if __builtin_implicit_as_convert is called directly.
|
|
|
+ auto conversion_failed = [&] {
|
|
|
+ return ProgramError(source->source_loc())
|
|
|
+ << "no builtin conversion from " << *source_type << " to "
|
|
|
+ << *destination << " is known";
|
|
|
+ };
|
|
|
+
|
|
|
+ // Note that the conversion expression that we build may evaluate `source`
|
|
|
+ // more than once. This is OK because the __builtin_implicit_as_convert
|
|
|
+ // intrinsic is only intended to be called from within the prelude's impl of
|
|
|
+ // ImplicitAs, where `source` has no side effects.
|
|
|
+
|
|
|
+ switch (source_type->kind()) {
|
|
|
+ case Value::Kind::StructType:
|
|
|
+ switch (destination->kind()) {
|
|
|
+ case Value::Kind::StructType: {
|
|
|
+ llvm::ArrayRef<NamedValue> source_fields =
|
|
|
+ cast<StructType>(*source_type).fields();
|
|
|
+ llvm::ArrayRef<NamedValue> destination_fields =
|
|
|
+ cast<StructType>(*destination).fields();
|
|
|
+ // Ensure every source field exists in the destination type.
|
|
|
+ for (const auto& source_field : source_fields) {
|
|
|
+ if (!FindField(destination_fields, source_field.name)) {
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Initialize every destination field.
|
|
|
+ std::vector<FieldInitializer> result_fields;
|
|
|
+ for (const auto& destination_field : destination_fields) {
|
|
|
+ std::optional<NamedValue> source_field =
|
|
|
+ FindField(source_fields, destination_field.name);
|
|
|
+ if (!source_field.has_value()) {
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ auto* elem = arena_->New<SimpleMemberAccessExpression>(
|
|
|
+ source->source_loc(), source, source_field->name);
|
|
|
+ CARBON_RETURN_IF_ERROR(TypeCheckExp(elem, impl_scope));
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ Nonnull<Expression*> converted,
|
|
|
+ ImplicitlyConvert("implicit conversion", impl_scope, elem,
|
|
|
+ destination_field.value));
|
|
|
+ result_fields.push_back(
|
|
|
+ FieldInitializer(destination_field.name, converted));
|
|
|
+ }
|
|
|
+ auto* result = arena_->New<StructLiteral>(source->source_loc(),
|
|
|
+ std::move(result_fields));
|
|
|
+ CARBON_RETURN_IF_ERROR(TypeCheckExp(result, impl_scope));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ case Value::Kind::NominalClassType: {
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ std::vector<NamedValue> field_types,
|
|
|
+ FieldTypesWithBase(cast<NominalClassType>(*destination)));
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ Nonnull<Expression*> result,
|
|
|
+ ImplicitlyConvert("implicit conversion", impl_scope, source,
|
|
|
+ arena_->New<StructType>(field_types)));
|
|
|
+ // Perform a builtin conversion from struct to class.
|
|
|
+ return make_builtin_conversion(result);
|
|
|
+ }
|
|
|
+ case Value::Kind::TypeType:
|
|
|
+ // A value of empty struct type implicitly converts to type `type`.
|
|
|
+ if (cast<StructType>(*source_type).fields().empty()) {
|
|
|
+ return make_builtin_conversion(source);
|
|
|
+ }
|
|
|
+ return conversion_failed();
|
|
|
+ default:
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ return conversion_failed();
|
|
|
+ case Value::Kind::TupleType: {
|
|
|
+ const auto& source_tuple = cast<TupleType>(*source_type);
|
|
|
+ switch (destination->kind()) {
|
|
|
+ case Value::Kind::TupleType: {
|
|
|
+ const auto& destination_tuple = cast<TupleType>(*destination);
|
|
|
+ if (source_tuple.elements().size() !=
|
|
|
+ destination_tuple.elements().size()) {
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ std::vector<Nonnull<Expression*>> converted_elements;
|
|
|
+ for (size_t i = 0; i < source_tuple.elements().size(); ++i) {
|
|
|
+ auto* elem = arena_->New<IndexExpression>(
|
|
|
+ source->source_loc(), source,
|
|
|
+ arena_->New<IntLiteral>(source->source_loc(), i));
|
|
|
+ CARBON_RETURN_IF_ERROR(TypeCheckExp(elem, impl_scope));
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ Nonnull<Expression*> converted,
|
|
|
+ ImplicitlyConvert("implicit conversion", impl_scope, elem,
|
|
|
+ destination_tuple.elements()[i]));
|
|
|
+ converted_elements.push_back(converted);
|
|
|
+ }
|
|
|
+ auto* result = arena_->New<TupleLiteral>(
|
|
|
+ source->source_loc(), std::move(converted_elements));
|
|
|
+ CARBON_RETURN_IF_ERROR(TypeCheckExp(result, impl_scope));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ case Value::Kind::StaticArrayType: {
|
|
|
+ const auto& destination_array = cast<StaticArrayType>(*destination);
|
|
|
+ // First, convert each tuple element to the array element type if
|
|
|
+ // necessary.
|
|
|
+ if (!std::all_of(source_tuple.elements().begin(),
|
|
|
+ source_tuple.elements().end(),
|
|
|
+ [&](Nonnull<const Value*> element_type) {
|
|
|
+ return TypeEqual(element_type,
|
|
|
+ &destination_array.element_type(),
|
|
|
+ std::nullopt);
|
|
|
+ })) {
|
|
|
+ auto* destination_tuple_type = arena_->New<TupleType>(std::vector(
|
|
|
+ destination_array.size(), &destination_array.element_type()));
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ source, BuildBuiltinConversion(source, destination_tuple_type,
|
|
|
+ impl_scope));
|
|
|
+ }
|
|
|
+ // Perform a builtin conversion from tuple to array.
|
|
|
+ return make_builtin_conversion(source);
|
|
|
+ }
|
|
|
+ case Value::Kind::TypeType: {
|
|
|
+ // First, convert each tuple element to 'type' if necessary.
|
|
|
+ if (!std::all_of(source_tuple.elements().begin(),
|
|
|
+ source_tuple.elements().end(),
|
|
|
+ [](Nonnull<const Value*> element_type) {
|
|
|
+ return isa<TypeType>(element_type);
|
|
|
+ })) {
|
|
|
+ auto* destination_tuple_type = arena_->New<TupleType>(
|
|
|
+ std::vector(source_tuple.elements().size(), destination));
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ source, BuildBuiltinConversion(source, destination_tuple_type,
|
|
|
+ impl_scope));
|
|
|
+ }
|
|
|
+ // Perform a builtin conversion from tuple of types to type.
|
|
|
+ return make_builtin_conversion(source);
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ case Value::Kind::PointerType: {
|
|
|
+ if (destination->kind() != Value::Kind::PointerType) {
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ const auto* src_ptr = cast<PointerType>(source_type);
|
|
|
+ const auto* dest_ptr = cast<PointerType>(destination);
|
|
|
+ if (src_ptr->pointee_type().kind() != Value::Kind::NominalClassType ||
|
|
|
+ dest_ptr->pointee_type().kind() != Value::Kind::NominalClassType) {
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ const auto& src_class = cast<NominalClassType>(src_ptr->pointee_type());
|
|
|
+ if (src_class.InheritsClass(&dest_ptr->pointee_type())) {
|
|
|
+ return BuildSubtypeConversion(source, src_ptr, dest_ptr);
|
|
|
+ }
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return conversion_failed();
|
|
|
+ }
|
|
|
+
|
|
|
+ CARBON_FATAL() << "unreachable";
|
|
|
+}
|
|
|
+
|
|
|
auto TypeChecker::ImplicitlyConvert(std::string_view context,
|
|
|
const ImplScope& impl_scope,
|
|
|
Nonnull<Expression*> source,
|
|
|
@@ -774,79 +956,106 @@ auto TypeChecker::ImplicitlyConvert(std::string_view context,
|
|
|
Nonnull<const Value*> source_type = &source->static_type();
|
|
|
|
|
|
CARBON_RETURN_IF_ERROR(
|
|
|
- ExpectNonPlaceholderType(source->source_loc(), &source->static_type()));
|
|
|
+ ExpectNonPlaceholderType(source->source_loc(), source_type));
|
|
|
|
|
|
- if (TypeEqual(&source->static_type(), destination, std::nullopt)) {
|
|
|
+ if (TypeEqual(source_type, destination, std::nullopt)) {
|
|
|
// No conversions are required.
|
|
|
return source;
|
|
|
}
|
|
|
|
|
|
- // 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.
|
|
|
- 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.
|
|
|
+ // Conversion from a tuple of types to the type `type` is used in the prelude
|
|
|
+ // before the intrinsic impl of `ImplicitAs` is declared. We also need to do
|
|
|
+ // this as a prerequisite to the conversion of tuples to constrained types
|
|
|
+ // below.
|
|
|
+ if (isa<TupleType>(source_type) && IsTypeOfType(destination)) {
|
|
|
+ auto* type_type = arena_->New<TypeType>();
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ bool convertible,
|
|
|
+ IsBuiltinConversion(source_type, type_type, impl_scope,
|
|
|
+ /*allow_user_defined_conversions=*/true));
|
|
|
+ if (convertible) {
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ source, BuildBuiltinConversion(source, type_type, impl_scope));
|
|
|
+ source_type = &source->static_type();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // A type of type can be converted to another type of type if the value of
|
|
|
+ // the former satisfies the constraints of the latter. This conversion
|
|
|
+ // depends on the value, not only the type, so isn't supported by
|
|
|
+ // `ImplicitAs`.
|
|
|
+ if (IsTypeOfType(source_type) && IsTypeOfType(destination)) {
|
|
|
+ // Don't require the source value to be constant if the destination is
|
|
|
+ // `type`.
|
|
|
// TODO: Instead of excluding the special case where the destination is
|
|
|
// `type`, we should check if the source type has a subset of the
|
|
|
// constraints of the destination type. In that case, the source should not
|
|
|
- // be required to be constant.
|
|
|
- if (IsTypeOfType(destination) && !isa<TypeType>(destination)) {
|
|
|
- // First convert the source expression to type `type`.
|
|
|
- CARBON_ASSIGN_OR_RETURN(Nonnull<Expression*> source_as_type,
|
|
|
- ImplicitlyConvert(context, impl_scope, source,
|
|
|
- arena_->New<TypeType>()));
|
|
|
- CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> converted_value,
|
|
|
- InterpExp(source_as_type));
|
|
|
- CARBON_ASSIGN_OR_RETURN(
|
|
|
- Nonnull<const ConstraintType*> destination_constraint,
|
|
|
- ConvertToConstraintType(source->source_loc(), "implicit conversion",
|
|
|
- destination));
|
|
|
- destination = destination_constraint;
|
|
|
- if (trace_stream_->is_enabled()) {
|
|
|
- *trace_stream_ << "converting type " << *converted_value
|
|
|
- << " to constraint " << *destination_constraint
|
|
|
- << " for " << context << " in scope " << impl_scope
|
|
|
- << "\n";
|
|
|
- }
|
|
|
- // Note, we discard the witness. We don't actually need it in order to
|
|
|
- // perform the conversion, but we do want to know it exists.
|
|
|
- // TODO: A value of constraint type should carry both the type and the
|
|
|
- // witness.
|
|
|
- CARBON_RETURN_IF_ERROR(impl_scope.Resolve(destination_constraint,
|
|
|
- converted_value,
|
|
|
- source->source_loc(), *this));
|
|
|
- return arena_->New<ValueLiteral>(source->source_loc(), converted_value,
|
|
|
- destination_constraint,
|
|
|
- ExpressionCategory::Value);
|
|
|
- }
|
|
|
-
|
|
|
- if (IsTypeOfType(source_type) && IsTypeOfType(destination)) {
|
|
|
- // No conversion is required.
|
|
|
+ // be required to be constant. That case should also be supported by
|
|
|
+ // `ImplicitAs`.
|
|
|
+ if (isa<TypeType>(destination)) {
|
|
|
return source;
|
|
|
}
|
|
|
|
|
|
- // Perform the builtin conversion.
|
|
|
- auto* convert_expr =
|
|
|
- arena_->New<BuiltinConvertExpression>(source, destination);
|
|
|
-
|
|
|
- // For subtyping, rewrite into successive `.base` accesses.
|
|
|
- if (isa<PointerType>(source_type) && isa<PointerType>(destination) &&
|
|
|
- cast<PointerType>(destination)->pointee_type().kind() ==
|
|
|
- Value::Kind::NominalClassType) {
|
|
|
- CARBON_ASSIGN_OR_RETURN(
|
|
|
- const auto* rewrite,
|
|
|
- BuildSubtypeConversion(source, cast<PointerType>(source_type),
|
|
|
- cast<PointerType>(destination)))
|
|
|
- convert_expr->set_rewritten_form(rewrite);
|
|
|
+ // First convert the source expression to type `type`.
|
|
|
+ CARBON_ASSIGN_OR_RETURN(Nonnull<Expression*> source_as_type,
|
|
|
+ ImplicitlyConvert(context, impl_scope, source,
|
|
|
+ arena_->New<TypeType>()));
|
|
|
+ CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> converted_value,
|
|
|
+ InterpExp(source_as_type));
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ Nonnull<const ConstraintType*> destination_constraint,
|
|
|
+ ConvertToConstraintType(source->source_loc(), "implicit conversion",
|
|
|
+ destination));
|
|
|
+ destination = destination_constraint;
|
|
|
+ if (trace_stream_->is_enabled()) {
|
|
|
+ *trace_stream_ << "converting type " << *converted_value
|
|
|
+ << " to constraint " << *destination_constraint << " for "
|
|
|
+ << context << " in scope " << impl_scope << "\n";
|
|
|
+ }
|
|
|
+ // Note, we discard the witness. We don't actually need it in order to
|
|
|
+ // perform the conversion, but we do want to know it exists.
|
|
|
+ // TODO: A value of constraint type should carry both the type and the
|
|
|
+ // witness.
|
|
|
+ CARBON_RETURN_IF_ERROR(impl_scope.Resolve(
|
|
|
+ destination_constraint, converted_value, source->source_loc(), *this));
|
|
|
+ return arena_->New<ValueLiteral>(source->source_loc(), converted_value,
|
|
|
+ destination_constraint,
|
|
|
+ ExpressionCategory::Value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Conversion from a tuple literal to a tuple type converts each element in
|
|
|
+ // turn, rather than converting the tuple as a whole. This is important in
|
|
|
+ // order to evaluate arguments to a function call in a reasonable order, and
|
|
|
+ // this conversion needs to be built-in because we use it while type-checking
|
|
|
+ // the prelude.
|
|
|
+ if (auto* source_tuple = dyn_cast<TupleLiteral>(source)) {
|
|
|
+ if (auto* destination_tuple = dyn_cast<TupleType>(destination)) {
|
|
|
+ if (source_tuple->fields().size() !=
|
|
|
+ destination_tuple->elements().size()) {
|
|
|
+ return ProgramError(source->source_loc())
|
|
|
+ << "type error in " << context << ": `" << *source_type << "`"
|
|
|
+ << " is not implicitly convertible to tuple type "
|
|
|
+ << "`" << *destination << "` of different length";
|
|
|
+ }
|
|
|
+ std::vector<Nonnull<Expression*>> converted_elements;
|
|
|
+ for (size_t i = 0; i < source_tuple->fields().size(); ++i) {
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ Nonnull<Expression*> converted,
|
|
|
+ ImplicitlyConvert("implicit conversion", impl_scope,
|
|
|
+ source_tuple->fields()[i],
|
|
|
+ destination_tuple->elements()[i]));
|
|
|
+ converted_elements.push_back(converted);
|
|
|
+ }
|
|
|
+ auto* result = arena_->New<TupleLiteral>(source->source_loc(),
|
|
|
+ std::move(converted_elements));
|
|
|
+ // TODO: Should be ExpressionCategory::Initializing.
|
|
|
+ result->set_expression_category(ExpressionCategory::Value);
|
|
|
+ result->set_static_type(destination);
|
|
|
+ return result;
|
|
|
}
|
|
|
-
|
|
|
- return convert_expr;
|
|
|
}
|
|
|
|
|
|
+ // Build a call to the conversion function.
|
|
|
ErrorOr<Nonnull<Expression*>> converted = BuildBuiltinMethodCall(
|
|
|
impl_scope, source,
|
|
|
BuiltinInterfaceName{Builtin::ImplicitAs, destination},
|
|
|
@@ -869,9 +1078,15 @@ auto TypeChecker::IsIntrinsicConstraintSatisfied(
|
|
|
case IntrinsicConstraint::ImplicitAs:
|
|
|
CARBON_CHECK(constraint.arguments.size() == 1)
|
|
|
<< "wrong number of arguments for `__intrinsic_implicit_as`";
|
|
|
- return IsImplicitlyConvertible(constraint.type, constraint.arguments[0],
|
|
|
- impl_scope,
|
|
|
- /*allow_user_defined_conversions=*/false);
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ bool convertible,
|
|
|
+ IsBuiltinConversion(constraint.type, constraint.arguments[0],
|
|
|
+ impl_scope,
|
|
|
+ /*allow_user_defined_conversions=*/true));
|
|
|
+ if (trace_stream_->is_enabled()) {
|
|
|
+ *trace_stream_ << constraint << " evaluated to " << convertible << "\n";
|
|
|
+ }
|
|
|
+ return convertible;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1346,20 +1561,6 @@ auto TypeChecker::ArgumentDeduction::Finish(
|
|
|
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(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
|
|
|
// intentionally don't consider equality constraints here; we need the
|
|
|
// same symbolic type, otherwise it would be ambiguous which spelling
|
|
|
@@ -3745,10 +3946,10 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
|
|
|
}
|
|
|
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> result,
|
|
|
TypeCheckTypeExp(args[1], impl_scope));
|
|
|
- // TODO: Check that the type of args[0] implicitly converts to
|
|
|
- // args[1].
|
|
|
- e->set_static_type(result);
|
|
|
- e->set_expression_category(ExpressionCategory::Value);
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
+ Nonnull<Expression*> converted,
|
|
|
+ BuildBuiltinConversion(args[0], result, impl_scope));
|
|
|
+ cast<IntrinsicExpression>(e)->set_rewritten_form(converted);
|
|
|
return Success();
|
|
|
}
|
|
|
case IntrinsicExpression::Intrinsic::IntEq: {
|
|
|
@@ -5707,7 +5908,7 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
|
|
|
// processing the interface, in case the interface expression uses `Self`.
|
|
|
Nonnull<SelfDeclaration*> self = impl_decl->self();
|
|
|
self->set_constant_value(impl_type_value);
|
|
|
- self->set_static_type(&impl_decl->impl_type()->static_type());
|
|
|
+ self->set_static_type(arena_->New<TypeType>());
|
|
|
|
|
|
// Check and interpret the interface.
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
@@ -5724,7 +5925,7 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
|
|
|
{
|
|
|
// TODO: Combine this with the SelfDeclaration.
|
|
|
auto* self_binding = arena_->New<GenericBinding>(
|
|
|
- self->source_loc(), "Self", impl_decl->impl_type(),
|
|
|
+ self->source_loc(), "Self", &impl_decl->interface(),
|
|
|
GenericBinding::BindingKind::Checked);
|
|
|
self_binding->set_symbolic_identity(impl_type_value);
|
|
|
self_binding->set_value(impl_type_value);
|