Skip to content

Commit 30b8a93

Browse files
zygoloidjosh11b
andauthored
Support conversion from T* to const T*. (#5971)
Also support conversion from Derived* to const Base*. --------- Co-authored-by: josh11b <[email protected]>
1 parent ad84e71 commit 30b8a93

File tree

10 files changed

+580
-315
lines changed

10 files changed

+580
-315
lines changed

toolchain/check/convert.cpp

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -673,13 +673,20 @@ static auto ConvertDerivedToBase(Context& context, SemIR::LocId loc_id,
673673
// Materialize a temporary if necessary.
674674
value_id = ConvertToValueOrRefExpr(context, value_id);
675675

676+
// Preserve type qualifiers.
677+
auto quals = context.types()
678+
.GetUnqualifiedTypeAndQualifiers(
679+
context.insts().Get(value_id).type_id())
680+
.second;
681+
676682
// Add a series of `.base` accesses.
677683
for (auto [base_id, base_type_id] : path) {
678684
auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(base_id);
679-
value_id = AddInst<SemIR::ClassElementAccess>(context, loc_id,
680-
{.type_id = base_type_id,
681-
.base_id = value_id,
682-
.index = base_decl.index});
685+
value_id = AddInst<SemIR::ClassElementAccess>(
686+
context, loc_id,
687+
{.type_id = GetQualifiedType(context, base_type_id, quals),
688+
.base_id = value_id,
689+
.index = base_decl.index});
683690
}
684691
return value_id;
685692
}
@@ -689,13 +696,13 @@ static auto ConvertDerivedPointerToBasePointer(
689696
Context& context, SemIR::LocId loc_id, SemIR::PointerType src_ptr_type,
690697
SemIR::TypeId dest_ptr_type_id, SemIR::InstId ptr_id,
691698
const InheritancePath& path) -> SemIR::InstId {
699+
auto pointee_type_id =
700+
context.types().GetTypeIdForTypeInstId(src_ptr_type.pointee_id);
701+
692702
// Form `*p`.
693703
ptr_id = ConvertToValueExpr(context, ptr_id);
694704
auto ref_id = AddInst<SemIR::Deref>(
695-
context, loc_id,
696-
{.type_id =
697-
context.types().GetTypeIdForTypeInstId(src_ptr_type.pointee_id),
698-
.pointer_id = ptr_id});
705+
context, loc_id, {.type_id = pointee_type_id, .pointer_id = ptr_id});
699706

700707
// Convert as a reference expression.
701708
ref_id = ConvertDerivedToBase(context, loc_id, ref_id, path);
@@ -950,6 +957,12 @@ static auto PerformBuiltinConversion(
950957
}
951958
}
952959

960+
// No other conversions apply when the source and destination types are the
961+
// same.
962+
if (value_type_id == target.type_id) {
963+
return value_id;
964+
}
965+
953966
// A tuple (T1, T2, ..., Tn) converts to array(T, n) if each Ti converts to T.
954967
if (auto target_array_type = target_type_inst.TryAs<SemIR::ArrayType>()) {
955968
if (auto src_tuple_type =
@@ -983,21 +996,50 @@ static auto PerformBuiltinConversion(
983996
}
984997
}
985998

986-
// A pointer T* converts to U* if T is a class derived from U.
999+
// A pointer T* converts to [const] U* if T is the same as U, or is a class
1000+
// derived from U.
9871001
if (auto target_pointer_type = target_type_inst.TryAs<SemIR::PointerType>()) {
9881002
if (auto src_pointer_type =
9891003
sem_ir.types().TryGetAs<SemIR::PointerType>(value_type_id)) {
990-
if (auto path =
991-
ComputeInheritancePath(context, loc_id,
992-
context.types().GetTypeIdForTypeInstId(
993-
src_pointer_type->pointee_id),
994-
context.types().GetTypeIdForTypeInstId(
995-
target_pointer_type->pointee_id));
996-
path && !path->empty()) {
997-
return ConvertDerivedPointerToBasePointer(
998-
context, loc_id, *src_pointer_type, target.type_id, value_id,
999-
*path);
1004+
auto [unqual_target_pointee_type_id, target_quals] =
1005+
sem_ir.types().GetUnqualifiedTypeAndQualifiers(
1006+
context.types().GetTypeIdForTypeInstId(
1007+
target_pointer_type->pointee_id));
1008+
auto [unqual_src_pointee_type_id, src_quals] =
1009+
sem_ir.types().GetUnqualifiedTypeAndQualifiers(
1010+
context.types().GetTypeIdForTypeInstId(
1011+
src_pointer_type->pointee_id));
1012+
1013+
// If the qualifiers are incompatible, we can't perform a conversion.
1014+
if ((src_quals & ~target_quals) != SemIR::TypeQualifiers::None) {
1015+
// TODO: Consider producing a custom diagnostic here for a cast that
1016+
// discards constness. We should allow this with `unsafe as`.
1017+
return value_id;
10001018
}
1019+
1020+
if (unqual_target_pointee_type_id != unqual_src_pointee_type_id) {
1021+
// If there's an inheritance path from target to source, this is a
1022+
// derived to base conversion.
1023+
if (auto path = ComputeInheritancePath(context, loc_id,
1024+
unqual_src_pointee_type_id,
1025+
unqual_target_pointee_type_id);
1026+
path && !path->empty()) {
1027+
value_id = ConvertDerivedPointerToBasePointer(
1028+
context, loc_id, *src_pointer_type, target.type_id, value_id,
1029+
*path);
1030+
} else {
1031+
// No conversion was possible.
1032+
return value_id;
1033+
}
1034+
}
1035+
1036+
// Perform a compatible conversion to add any new qualifiers.
1037+
if (src_quals != target_quals) {
1038+
return AddInst<SemIR::AsCompatible>(
1039+
context, loc_id,
1040+
{.type_id = target.type_id, .source_id = value_id});
1041+
}
1042+
return value_id;
10011043
}
10021044
}
10031045

0 commit comments

Comments
 (0)