Skip to content

Commit dcd7471

Browse files
committed
[clang] p0388 conversion to incomplete array
This implements the new implicit conversion sequence to an incomplete (unbounded) array type. It is mostly Richard Smith's work, updated to trunk, testcases added and a few bugs fixed found in such testing. It is not a complete implementation of p0388. Differential Revision: https://reviews.llvm.org/D102645
1 parent a76cfc2 commit dcd7471

File tree

11 files changed

+262
-90
lines changed

11 files changed

+262
-90
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2535,8 +2535,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
25352535
bool ObjCMethodsAreEqual(const ObjCMethodDecl *MethodDecl,
25362536
const ObjCMethodDecl *MethodImp);
25372537

2538-
bool UnwrapSimilarTypes(QualType &T1, QualType &T2);
2539-
void UnwrapSimilarArrayTypes(QualType &T1, QualType &T2);
2538+
bool UnwrapSimilarTypes(QualType &T1, QualType &T2,
2539+
bool AllowPiMismatch = true);
2540+
void UnwrapSimilarArrayTypes(QualType &T1, QualType &T2,
2541+
bool AllowPiMismatch = true);
25402542

25412543
/// Determine if two types are similar, according to the C++ rules. That is,
25422544
/// determine if they are the same other than qualifiers on the initial

clang/lib/AST/ASTContext.cpp

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5844,7 +5844,11 @@ QualType ASTContext::getUnqualifiedArrayType(QualType type,
58445844
/// Attempt to unwrap two types that may both be array types with the same bound
58455845
/// (or both be array types of unknown bound) for the purpose of comparing the
58465846
/// cv-decomposition of two types per C++ [conv.qual].
5847-
void ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2) {
5847+
///
5848+
/// \param AllowPiMismatch Allow the Pi1 and Pi2 to differ as described in
5849+
/// C++20 [conv.qual], if permitted by the current language mode.
5850+
void ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2,
5851+
bool AllowPiMismatch) {
58485852
while (true) {
58495853
auto *AT1 = getAsArrayType(T1);
58505854
if (!AT1)
@@ -5856,12 +5860,21 @@ void ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2) {
58565860

58575861
// If we don't have two array types with the same constant bound nor two
58585862
// incomplete array types, we've unwrapped everything we can.
5863+
// C++20 also permits one type to be a constant array type and the other
5864+
// to be an incomplete array type.
5865+
// FIXME: Consider also unwrapping array of unknown bound and VLA.
58595866
if (auto *CAT1 = dyn_cast<ConstantArrayType>(AT1)) {
58605867
auto *CAT2 = dyn_cast<ConstantArrayType>(AT2);
5861-
if (!CAT2 || CAT1->getSize() != CAT2->getSize())
5868+
if (!(CAT2 && CAT1->getSize() == CAT2->getSize()) &&
5869+
!(getLangOpts().CPlusPlus20 && AllowPiMismatch &&
5870+
isa<IncompleteArrayType>(AT2)))
5871+
return;
5872+
} else if (isa<IncompleteArrayType>(AT1)) {
5873+
if (!isa<IncompleteArrayType>(AT2) &&
5874+
!(getLangOpts().CPlusPlus20 && AllowPiMismatch &&
5875+
isa<ConstantArrayType>(AT2)))
58625876
return;
5863-
} else if (!isa<IncompleteArrayType>(AT1) ||
5864-
!isa<IncompleteArrayType>(AT2)) {
5877+
} else {
58655878
return;
58665879
}
58675880

@@ -5880,10 +5893,14 @@ void ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2) {
58805893
/// "unwraps" pointer and pointer-to-member types to compare them at each
58815894
/// level.
58825895
///
5896+
/// \param AllowPiMismatch Allow the Pi1 and Pi2 to differ as described in
5897+
/// C++20 [conv.qual], if permitted by the current language mode.
5898+
///
58835899
/// \return \c true if a pointer type was unwrapped, \c false if we reached a
58845900
/// pair of types that can't be unwrapped further.
5885-
bool ASTContext::UnwrapSimilarTypes(QualType &T1, QualType &T2) {
5886-
UnwrapSimilarArrayTypes(T1, T2);
5901+
bool ASTContext::UnwrapSimilarTypes(QualType &T1, QualType &T2,
5902+
bool AllowPiMismatch) {
5903+
UnwrapSimilarArrayTypes(T1, T2, AllowPiMismatch);
58875904

58885905
const auto *T1PtrType = T1->getAs<PointerType>();
58895906
const auto *T2PtrType = T2->getAs<PointerType>();
@@ -5944,7 +5961,7 @@ bool ASTContext::hasCvrSimilarType(QualType T1, QualType T2) {
59445961
if (hasSameType(T1, T2))
59455962
return true;
59465963

5947-
if (!UnwrapSimilarTypes(T1, T2))
5964+
if (!UnwrapSimilarTypes(T1, T2, /*AllowPiMismatch*/ false))
59485965
return false;
59495966
}
59505967
}

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4683,10 +4683,28 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
46834683
case CK_UserDefinedConversion:
46844684
case CK_CPointerToObjCPointerCast:
46854685
case CK_BlockPointerToObjCPointerCast:
4686-
case CK_NoOp:
46874686
case CK_LValueToRValue:
46884687
return EmitLValue(E->getSubExpr());
46894688

4689+
case CK_NoOp: {
4690+
// CK_NoOp can model a qualification conversion, which can remove an array
4691+
// bound and change the IR type.
4692+
// FIXME: Once pointee types are removed from IR, remove this.
4693+
LValue LV = EmitLValue(E->getSubExpr());
4694+
if (LV.isSimple()) {
4695+
Address V = LV.getAddress(*this);
4696+
if (V.isValid()) {
4697+
llvm::Type *T =
4698+
ConvertTypeForMem(E->getType())
4699+
->getPointerTo(
4700+
cast<llvm::PointerType>(V.getType())->getAddressSpace());
4701+
if (V.getType() != T)
4702+
LV.setAddress(Builder.CreateBitCast(V, T));
4703+
}
4704+
}
4705+
return LV;
4706+
}
4707+
46904708
case CK_UncheckedDerivedToBase:
46914709
case CK_DerivedToBase: {
46924710
const auto *DerivedClassTy =

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2154,10 +2154,22 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
21542154
}
21552155
case CK_AtomicToNonAtomic:
21562156
case CK_NonAtomicToAtomic:
2157-
case CK_NoOp:
21582157
case CK_UserDefinedConversion:
21592158
return Visit(const_cast<Expr*>(E));
21602159

2160+
case CK_NoOp: {
2161+
llvm::Value *V = Visit(const_cast<Expr *>(E));
2162+
if (V) {
2163+
// CK_NoOp can model a pointer qualification conversion, which can remove
2164+
// an array bound and change the IR type.
2165+
// FIXME: Once pointee types are removed from IR, remove this.
2166+
llvm::Type *T = ConvertType(DestTy);
2167+
if (T != V->getType())
2168+
V = Builder.CreateBitCast(V, T);
2169+
}
2170+
return V;
2171+
}
2172+
21612173
case CK_BaseToDerived: {
21622174
const CXXRecordDecl *DerivedClassDecl = DestTy->getPointeeCXXRecordDecl();
21632175
assert(DerivedClassDecl && "BaseToDerived arg isn't a C++ object pointer!");

clang/lib/CodeGen/CGObjC.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,6 +1555,12 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
15551555
argCK = CK_AnyPointerToBlockPointerCast;
15561556
} else if (ivarRef.getType()->isPointerType()) {
15571557
argCK = CK_BitCast;
1558+
} else if (argLoad.getType()->isAtomicType() &&
1559+
!ivarRef.getType()->isAtomicType()) {
1560+
argCK = CK_AtomicToNonAtomic;
1561+
} else if (!argLoad.getType()->isAtomicType() &&
1562+
ivarRef.getType()->isAtomicType()) {
1563+
argCK = CK_NonAtomicToAtomic;
15581564
}
15591565
ImplicitCastExpr argCast(ImplicitCastExpr::OnStack, ivarRef.getType(), argCK,
15601566
&argLoad, VK_PRValue, FPOptionsOverride());

clang/lib/Sema/SemaCast.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,9 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr,
13131313
// lvalue-to-rvalue, array-to-pointer, function-to-pointer, and boolean
13141314
// conversions, subject to further restrictions.
13151315
// Also, C++ 5.2.9p1 forbids casting away constness, which makes reversal
1316-
// of qualification conversions impossible.
1316+
// of qualification conversions impossible. (In C++20, adding an array bound
1317+
// would be the reverse of a qualification conversion, but adding permission
1318+
// to add an array bound in a static_cast is a wording oversight.)
13171319
// In the CStyle case, the earlier attempt to const_cast should have taken
13181320
// care of reverse qualification conversions.
13191321

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6711,6 +6711,36 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
67116711
}
67126712

67136713
// FIXME: Can we unify the following with UnwrapSimilarTypes?
6714+
6715+
const ArrayType *Arr1, *Arr2;
6716+
if ((Arr1 = Context.getAsArrayType(Composite1)) &&
6717+
(Arr2 = Context.getAsArrayType(Composite2))) {
6718+
auto *CAT1 = dyn_cast<ConstantArrayType>(Arr1);
6719+
auto *CAT2 = dyn_cast<ConstantArrayType>(Arr2);
6720+
if (CAT1 && CAT2 && CAT1->getSize() == CAT2->getSize()) {
6721+
Composite1 = Arr1->getElementType();
6722+
Composite2 = Arr2->getElementType();
6723+
Steps.emplace_back(Step::Array, CAT1);
6724+
continue;
6725+
}
6726+
bool IAT1 = isa<IncompleteArrayType>(Arr1);
6727+
bool IAT2 = isa<IncompleteArrayType>(Arr2);
6728+
if ((IAT1 && IAT2) ||
6729+
(getLangOpts().CPlusPlus20 && (IAT1 != IAT2) &&
6730+
((bool)CAT1 != (bool)CAT2) &&
6731+
(Steps.empty() || Steps.back().K != Step::Array))) {
6732+
// In C++20 onwards, we can unify an array of N T with an array of
6733+
// a different or unknown bound. But we can't form an array whose
6734+
// element type is an array of unknown bound by doing so.
6735+
Composite1 = Arr1->getElementType();
6736+
Composite2 = Arr2->getElementType();
6737+
Steps.emplace_back(Step::Array);
6738+
if (CAT1 || CAT2)
6739+
NeedConstBefore = Steps.size();
6740+
continue;
6741+
}
6742+
}
6743+
67146744
const PointerType *Ptr1, *Ptr2;
67156745
if ((Ptr1 = Composite1->getAs<PointerType>()) &&
67166746
(Ptr2 = Composite2->getAs<PointerType>())) {
@@ -6771,8 +6801,6 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
67716801
continue;
67726802
}
67736803

6774-
// FIXME: arrays
6775-
67766804
// FIXME: block pointer types?
67776805

67786806
// Cannot unwrap any more types.

clang/lib/Sema/SemaOverload.cpp

Lines changed: 45 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3246,6 +3246,19 @@ static bool isQualificationConversionStep(QualType FromType, QualType ToType,
32463246
!PreviousToQualsIncludeConst)
32473247
return false;
32483248

3249+
// The following wording is from C++20, where the result of the conversion
3250+
// is T3, not T2.
3251+
// -- if [...] P1,i [...] is "array of unknown bound of", P3,i is
3252+
// "array of unknown bound of"
3253+
if (FromType->isIncompleteArrayType() && !ToType->isIncompleteArrayType())
3254+
return false;
3255+
3256+
// -- if the resulting P3,i is different from P1,i [...], then const is
3257+
// added to every cv 3_k for 0 < k < i.
3258+
if (!CStyle && FromType->isConstantArrayType() &&
3259+
ToType->isIncompleteArrayType() && !PreviousToQualsIncludeConst)
3260+
return false;
3261+
32493262
// Keep track of whether all prior cv-qualifiers in the "to" type
32503263
// include const.
32513264
PreviousToQualsIncludeConst =
@@ -4199,12 +4212,15 @@ static ImplicitConversionSequence::CompareKind
41994212
CompareQualificationConversions(Sema &S,
42004213
const StandardConversionSequence& SCS1,
42014214
const StandardConversionSequence& SCS2) {
4202-
// C++ 13.3.3.2p3:
4215+
// C++ [over.ics.rank]p3:
42034216
// -- S1 and S2 differ only in their qualification conversion and
4204-
// yield similar types T1 and T2 (C++ 4.4), respectively, and the
4205-
// cv-qualification signature of type T1 is a proper subset of
4206-
// the cv-qualification signature of type T2, and S1 is not the
4217+
// yield similar types T1 and T2 (C++ 4.4), respectively, [...]
4218+
// [C++98]
4219+
// [...] and the cv-qualification signature of type T1 is a proper subset
4220+
// of the cv-qualification signature of type T2, and S1 is not the
42074221
// deprecated string literal array-to-pointer conversion (4.2).
4222+
// [C++2a]
4223+
// [...] where T1 can be converted to T2 by a qualification conversion.
42084224
if (SCS1.First != SCS2.First || SCS1.Second != SCS2.Second ||
42094225
SCS1.Third != SCS2.Third || SCS1.Third != ICK_Qualification)
42104226
return ImplicitConversionSequence::Indistinguishable;
@@ -4225,79 +4241,35 @@ CompareQualificationConversions(Sema &S,
42254241
if (UnqualT1 == UnqualT2)
42264242
return ImplicitConversionSequence::Indistinguishable;
42274243

4228-
ImplicitConversionSequence::CompareKind Result
4229-
= ImplicitConversionSequence::Indistinguishable;
4244+
// Don't ever prefer a standard conversion sequence that uses the deprecated
4245+
// string literal array to pointer conversion.
4246+
bool CanPick1 = !SCS1.DeprecatedStringLiteralToCharPtr;
4247+
bool CanPick2 = !SCS2.DeprecatedStringLiteralToCharPtr;
42304248

42314249
// Objective-C++ ARC:
42324250
// Prefer qualification conversions not involving a change in lifetime
4233-
// to qualification conversions that do not change lifetime.
4234-
if (SCS1.QualificationIncludesObjCLifetime !=
4235-
SCS2.QualificationIncludesObjCLifetime) {
4236-
Result = SCS1.QualificationIncludesObjCLifetime
4237-
? ImplicitConversionSequence::Worse
4238-
: ImplicitConversionSequence::Better;
4239-
}
4240-
4241-
while (S.Context.UnwrapSimilarTypes(T1, T2)) {
4242-
// Within each iteration of the loop, we check the qualifiers to
4243-
// determine if this still looks like a qualification
4244-
// conversion. Then, if all is well, we unwrap one more level of
4245-
// pointers or pointers-to-members and do it all again
4246-
// until there are no more pointers or pointers-to-members left
4247-
// to unwrap. This essentially mimics what
4248-
// IsQualificationConversion does, but here we're checking for a
4249-
// strict subset of qualifiers.
4250-
if (T1.getQualifiers().withoutObjCLifetime() ==
4251-
T2.getQualifiers().withoutObjCLifetime())
4252-
// The qualifiers are the same, so this doesn't tell us anything
4253-
// about how the sequences rank.
4254-
// ObjC ownership quals are omitted above as they interfere with
4255-
// the ARC overload rule.
4256-
;
4257-
else if (T2.isMoreQualifiedThan(T1)) {
4258-
// T1 has fewer qualifiers, so it could be the better sequence.
4259-
if (Result == ImplicitConversionSequence::Worse)
4260-
// Neither has qualifiers that are a subset of the other's
4261-
// qualifiers.
4262-
return ImplicitConversionSequence::Indistinguishable;
4263-
4264-
Result = ImplicitConversionSequence::Better;
4265-
} else if (T1.isMoreQualifiedThan(T2)) {
4266-
// T2 has fewer qualifiers, so it could be the better sequence.
4267-
if (Result == ImplicitConversionSequence::Better)
4268-
// Neither has qualifiers that are a subset of the other's
4269-
// qualifiers.
4270-
return ImplicitConversionSequence::Indistinguishable;
4271-
4272-
Result = ImplicitConversionSequence::Worse;
4273-
} else {
4274-
// Qualifiers are disjoint.
4275-
return ImplicitConversionSequence::Indistinguishable;
4276-
}
4277-
4278-
// If the types after this point are equivalent, we're done.
4279-
if (S.Context.hasSameUnqualifiedType(T1, T2))
4280-
break;
4281-
}
4282-
4283-
// Check that the winning standard conversion sequence isn't using
4284-
// the deprecated string literal array to pointer conversion.
4285-
switch (Result) {
4286-
case ImplicitConversionSequence::Better:
4287-
if (SCS1.DeprecatedStringLiteralToCharPtr)
4288-
Result = ImplicitConversionSequence::Indistinguishable;
4289-
break;
4251+
// to qualification conversions that do change lifetime.
4252+
if (SCS1.QualificationIncludesObjCLifetime &&
4253+
!SCS2.QualificationIncludesObjCLifetime)
4254+
CanPick1 = false;
4255+
if (SCS2.QualificationIncludesObjCLifetime &&
4256+
!SCS1.QualificationIncludesObjCLifetime)
4257+
CanPick2 = false;
42904258

4291-
case ImplicitConversionSequence::Indistinguishable:
4292-
break;
4293-
4294-
case ImplicitConversionSequence::Worse:
4295-
if (SCS2.DeprecatedStringLiteralToCharPtr)
4296-
Result = ImplicitConversionSequence::Indistinguishable;
4297-
break;
4298-
}
4299-
4300-
return Result;
4259+
bool ObjCLifetimeConversion;
4260+
if (CanPick1 &&
4261+
!S.IsQualificationConversion(T1, T2, false, ObjCLifetimeConversion))
4262+
CanPick1 = false;
4263+
// FIXME: In Objective-C ARC, we can have qualification conversions in both
4264+
// directions, so we can't short-cut this second check in general.
4265+
if (CanPick2 &&
4266+
!S.IsQualificationConversion(T2, T1, false, ObjCLifetimeConversion))
4267+
CanPick2 = false;
4268+
4269+
if (CanPick1 != CanPick2)
4270+
return CanPick1 ? ImplicitConversionSequence::Better
4271+
: ImplicitConversionSequence::Worse;
4272+
return ImplicitConversionSequence::Indistinguishable;
43014273
}
43024274

43034275
/// CompareDerivedToBaseConversions - Compares two standard conversion

clang/test/CXX/drs/dr3xx.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,19 @@ namespace dr330 { // dr330: 7
373373
q = p; // ok
374374
q2 = p; // ok
375375
r = p; // expected-error {{incompatible}}
376-
s = p; // expected-error {{incompatible}} (for now)
376+
s = p;
377+
#if __cplusplus < 202002
378+
// expected-error@-2 {{incompatible}} (fixed by p0388)
379+
#endif
377380
t = p; // expected-error {{incompatible}}
378-
s = q; // expected-error {{incompatible}}
379-
s = q2; // expected-error {{incompatible}}
381+
s = q;
382+
#if __cplusplus < 202002
383+
// expected-error@-2 {{incompatible}} (fixed by p0388)
384+
#endif
385+
s = q2;
386+
#if __cplusplus < 202002
387+
// expected-error@-2 {{incompatible}} (fixed by p0388)
388+
#endif
380389
s = t; // ok, adding const
381390
t = s; // expected-error {{discards qualifiers}}
382391
(void) const_cast<P>(q);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang_cc1 %s -triple %itanium_abi_triple -std=c++20 -emit-llvm -O2 -o - | FileCheck %s
2+
3+
// p0388 conversions to unbounded array
4+
// dcl.init.list/3
5+
6+
namespace One {
7+
int ga[1];
8+
9+
// CHECK-LABEL: @_ZN3One5frob1Ev
10+
// CHECK-NEXT: entry:
11+
// CHECK-NEXT: ret [0 x i32]* bitcast ([1 x i32]* @_ZN3One2gaE to [0 x i32]*)
12+
auto &frob1() {
13+
int(&r1)[] = ga;
14+
15+
return r1;
16+
}
17+
18+
// CHECK-LABEL: @_ZN3One5frob2ERA1_i
19+
// CHECK-NEXT: entry:
20+
// CHECK-NEXT: %0 = bitcast [1 x i32]* %arp to [0 x i32]*
21+
// CHECK-NEXT: ret [0 x i32]* %0
22+
auto &frob2(int (&arp)[1]) {
23+
int(&r2)[] = arp;
24+
25+
return r2;
26+
}
27+
} // namespace One

0 commit comments

Comments
 (0)