|
|
@@ -1123,6 +1123,59 @@ static auto PerformCopy(Context& context, SemIR::InstId expr_id, bool diagnose)
|
|
|
return SemIR::ErrorInst::SingletonInstId;
|
|
|
}
|
|
|
|
|
|
+static auto DiagnoseConversionFailureToConstraintValue(Context& context,
|
|
|
+ SemIR::LocId loc_id,
|
|
|
+ SemIR::InstId expr_id,
|
|
|
+ ConversionTarget target)
|
|
|
+ -> DiagnosticBuilder {
|
|
|
+ CARBON_DCHECK(target.type_id == SemIR::TypeType::SingletonTypeId ||
|
|
|
+ context.types().Is<SemIR::FacetType>(target.type_id));
|
|
|
+
|
|
|
+ auto type_of_expr_id = context.insts().Get(expr_id).type_id();
|
|
|
+ if (context.types().IsFacetType(type_of_expr_id)) {
|
|
|
+ // If the source type is/has a facet value, then we can include its
|
|
|
+ // FacetType in the diagnostic to help explain what interfaces the
|
|
|
+ // source type implements.
|
|
|
+ auto facet_value_inst_id = SemIR::InstId::None;
|
|
|
+ if (auto facet_access_type =
|
|
|
+ context.insts().TryGetAs<SemIR::FacetAccessType>(expr_id)) {
|
|
|
+ facet_value_inst_id = facet_access_type->facet_value_inst_id;
|
|
|
+ } else if (context.types().Is<SemIR::FacetType>(type_of_expr_id)) {
|
|
|
+ facet_value_inst_id = expr_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (facet_value_inst_id.has_value()) {
|
|
|
+ CARBON_DIAGNOSTIC(
|
|
|
+ ConversionFailureFacetToFacet, Error,
|
|
|
+ "cannot{0:| implicitly} convert type {1} that implements {2} "
|
|
|
+ "into type implementing {3}{0: with `as`|}",
|
|
|
+ BoolAsSelect, InstIdAsType, TypeOfInstId, SemIR::TypeId);
|
|
|
+ return context.emitter().Build(
|
|
|
+ loc_id, ConversionFailureFacetToFacet,
|
|
|
+ target.kind == ConversionTarget::ExplicitAs, expr_id,
|
|
|
+ facet_value_inst_id, target.type_id);
|
|
|
+ } else {
|
|
|
+ CARBON_DIAGNOSTIC(ConversionFailureTypeToFacet, Error,
|
|
|
+ "cannot{0:| implicitly} convert type {1} "
|
|
|
+ "into type implementing {2}{0: with `as`|}",
|
|
|
+ BoolAsSelect, InstIdAsType, SemIR::TypeId);
|
|
|
+ return context.emitter().Build(
|
|
|
+ loc_id, ConversionFailureTypeToFacet,
|
|
|
+ target.kind == ConversionTarget::ExplicitAs, expr_id, target.type_id);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ CARBON_DIAGNOSTIC(
|
|
|
+ ConversionFailureNonTypeToFacet, Error,
|
|
|
+ "cannot{0:| implicitly} convert non-type value of type {1} "
|
|
|
+ "{2:to|into type implementing} {3}{0: with `as`|}",
|
|
|
+ BoolAsSelect, TypeOfInstId, BoolAsSelect, SemIR::TypeId);
|
|
|
+ return context.emitter().Build(
|
|
|
+ loc_id, ConversionFailureNonTypeToFacet,
|
|
|
+ target.kind == ConversionTarget::ExplicitAs, expr_id,
|
|
|
+ target.type_id == SemIR::TypeType::SingletonTypeId, target.type_id);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
|
|
ConversionTarget target) -> SemIR::InstId {
|
|
|
auto& sem_ir = context.sem_ir();
|
|
|
@@ -1208,19 +1261,24 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
|
|
|
if (!target.diagnose) {
|
|
|
return context.emitter().BuildSuppressed();
|
|
|
}
|
|
|
- // TODO: Should this message change to say "object of type" when
|
|
|
- // converting from a reference expression?
|
|
|
- CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error,
|
|
|
- "cannot implicitly convert value of type {0} to {1}",
|
|
|
- TypeOfInstId, SemIR::TypeId);
|
|
|
- CARBON_DIAGNOSTIC(ExplicitAsConversionFailure, Error,
|
|
|
- "cannot convert value of type {0} to {1} with `as`",
|
|
|
- TypeOfInstId, SemIR::TypeId);
|
|
|
- return context.emitter().Build(loc_id,
|
|
|
- target.kind == ConversionTarget::ExplicitAs
|
|
|
- ? ExplicitAsConversionFailure
|
|
|
- : ImplicitAsConversionFailure,
|
|
|
- expr_id, target.type_id);
|
|
|
+ if (target.type_id == SemIR::TypeType::SingletonTypeId ||
|
|
|
+ sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
|
|
|
+ // TODO: Move this to PerformBuiltinConversion(). See
|
|
|
+ // https://github.com/carbon-language/carbon-lang/issues/5122.
|
|
|
+ return DiagnoseConversionFailureToConstraintValue(context, loc_id,
|
|
|
+ expr_id, target);
|
|
|
+ } else {
|
|
|
+ // TODO: Should this message change to say "object of type" when
|
|
|
+ // converting from a reference expression?
|
|
|
+ CARBON_DIAGNOSTIC(ConversionFailure, Error,
|
|
|
+ "cannot{0:| implicitly} convert value of type {1} to "
|
|
|
+ "{2}{0: with `as`|}",
|
|
|
+ BoolAsSelect, TypeOfInstId, SemIR::TypeId);
|
|
|
+ return context.emitter().Build(
|
|
|
+ loc_id, ConversionFailure,
|
|
|
+ target.kind == ConversionTarget::ExplicitAs, expr_id,
|
|
|
+ target.type_id);
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
// Pull a value directly out of the initializer if possible and wanted.
|