diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2535,8 +2535,10 @@ bool ObjCMethodsAreEqual(const ObjCMethodDecl *MethodDecl, const ObjCMethodDecl *MethodImp); - bool UnwrapSimilarTypes(QualType &T1, QualType &T2); - void UnwrapSimilarArrayTypes(QualType &T1, QualType &T2); + bool UnwrapSimilarTypes(QualType &T1, QualType &T2, + bool AllowPiMismatch = true); + void UnwrapSimilarArrayTypes(QualType &T1, QualType &T2, + bool AllowPiMismatch = true); /// Determine if two types are similar, according to the C++ rules. That is, /// determine if they are the same other than qualifiers on the initial diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5844,7 +5844,11 @@ /// Attempt to unwrap two types that may both be array types with the same bound /// (or both be array types of unknown bound) for the purpose of comparing the /// cv-decomposition of two types per C++ [conv.qual]. -void ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2) { +/// +/// \param AllowPiMismatch Allow the Pi1 and Pi2 to differ as described in +/// C++20 [conv.qual], if permitted by the current language mode. +void ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2, + bool AllowPiMismatch) { while (true) { auto *AT1 = getAsArrayType(T1); if (!AT1) @@ -5856,12 +5860,21 @@ // If we don't have two array types with the same constant bound nor two // incomplete array types, we've unwrapped everything we can. + // C++20 also permits one type to be a constant array type and the other + // to be an incomplete array type. + // FIXME: Consider also unwrapping array of unknown bound and VLA. if (auto *CAT1 = dyn_cast(AT1)) { auto *CAT2 = dyn_cast(AT2); - if (!CAT2 || CAT1->getSize() != CAT2->getSize()) + if (!(CAT2 && CAT1->getSize() == CAT2->getSize()) && + !(getLangOpts().CPlusPlus20 && AllowPiMismatch && + isa(AT2))) + return; + } else if (isa(AT1)) { + if (!isa(AT2) && + !(getLangOpts().CPlusPlus20 && AllowPiMismatch && + isa(AT2))) return; - } else if (!isa(AT1) || - !isa(AT2)) { + } else { return; } @@ -5880,10 +5893,14 @@ /// "unwraps" pointer and pointer-to-member types to compare them at each /// level. /// +/// \param AllowPiMismatch Allow the Pi1 and Pi2 to differ as described in +/// C++20 [conv.qual], if permitted by the current language mode. +/// /// \return \c true if a pointer type was unwrapped, \c false if we reached a /// pair of types that can't be unwrapped further. -bool ASTContext::UnwrapSimilarTypes(QualType &T1, QualType &T2) { - UnwrapSimilarArrayTypes(T1, T2); +bool ASTContext::UnwrapSimilarTypes(QualType &T1, QualType &T2, + bool AllowPiMismatch) { + UnwrapSimilarArrayTypes(T1, T2, AllowPiMismatch); const auto *T1PtrType = T1->getAs(); const auto *T2PtrType = T2->getAs(); @@ -5944,7 +5961,7 @@ if (hasSameType(T1, T2)) return true; - if (!UnwrapSimilarTypes(T1, T2)) + if (!UnwrapSimilarTypes(T1, T2, /*AllowPiMismatch*/ false)) return false; } } diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4683,10 +4683,28 @@ case CK_UserDefinedConversion: case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: - case CK_NoOp: case CK_LValueToRValue: return EmitLValue(E->getSubExpr()); + case CK_NoOp: { + // CK_NoOp can model a qualification conversion, which can remove an array + // bound and change the IR type. + // FIXME: Once pointee types are removed from IR, remove this. + LValue LV = EmitLValue(E->getSubExpr()); + if (LV.isSimple()) { + Address V = LV.getAddress(*this); + if (V.isValid()) { + llvm::Type *T = + ConvertTypeForMem(E->getType()) + ->getPointerTo( + cast(V.getType())->getAddressSpace()); + if (V.getType() != T) + LV.setAddress(Builder.CreateBitCast(V, T)); + } + } + return LV; + } + case CK_UncheckedDerivedToBase: case CK_DerivedToBase: { const auto *DerivedClassTy = diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2154,10 +2154,22 @@ } case CK_AtomicToNonAtomic: case CK_NonAtomicToAtomic: - case CK_NoOp: case CK_UserDefinedConversion: return Visit(const_cast(E)); + case CK_NoOp: { + llvm::Value *V = Visit(const_cast(E)); + if (V) { + // CK_NoOp can model a pointer qualification conversion, which can remove + // an array bound and change the IR type. + // FIXME: Once pointee types are removed from IR, remove this. + llvm::Type *T = ConvertType(DestTy); + if (T != V->getType()) + V = Builder.CreateBitCast(V, T); + } + return V; + } + case CK_BaseToDerived: { const CXXRecordDecl *DerivedClassDecl = DestTy->getPointeeCXXRecordDecl(); assert(DerivedClassDecl && "BaseToDerived arg isn't a C++ object pointer!"); diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -1555,6 +1555,12 @@ argCK = CK_AnyPointerToBlockPointerCast; } else if (ivarRef.getType()->isPointerType()) { argCK = CK_BitCast; + } else if (argLoad.getType()->isAtomicType() && + !ivarRef.getType()->isAtomicType()) { + argCK = CK_AtomicToNonAtomic; + } else if (!argLoad.getType()->isAtomicType() && + ivarRef.getType()->isAtomicType()) { + argCK = CK_NonAtomicToAtomic; } ImplicitCastExpr argCast(ImplicitCastExpr::OnStack, ivarRef.getType(), argCK, &argLoad, VK_PRValue, FPOptionsOverride()); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1313,7 +1313,9 @@ // lvalue-to-rvalue, array-to-pointer, function-to-pointer, and boolean // conversions, subject to further restrictions. // Also, C++ 5.2.9p1 forbids casting away constness, which makes reversal - // of qualification conversions impossible. + // of qualification conversions impossible. (In C++20, adding an array bound + // would be the reverse of a qualification conversion, but adding permission + // to add an array bound in a static_cast is a wording oversight.) // In the CStyle case, the earlier attempt to const_cast should have taken // care of reverse qualification conversions. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -6711,6 +6711,36 @@ } // FIXME: Can we unify the following with UnwrapSimilarTypes? + + const ArrayType *Arr1, *Arr2; + if ((Arr1 = Context.getAsArrayType(Composite1)) && + (Arr2 = Context.getAsArrayType(Composite2))) { + auto *CAT1 = dyn_cast(Arr1); + auto *CAT2 = dyn_cast(Arr2); + if (CAT1 && CAT2 && CAT1->getSize() == CAT2->getSize()) { + Composite1 = Arr1->getElementType(); + Composite2 = Arr2->getElementType(); + Steps.emplace_back(Step::Array, CAT1); + continue; + } + bool IAT1 = isa(Arr1); + bool IAT2 = isa(Arr2); + if ((IAT1 && IAT2) || + (getLangOpts().CPlusPlus20 && (IAT1 != IAT2) && + ((bool)CAT1 != (bool)CAT2) && + (Steps.empty() || Steps.back().K != Step::Array))) { + // In C++20 onwards, we can unify an array of N T with an array of + // a different or unknown bound. But we can't form an array whose + // element type is an array of unknown bound by doing so. + Composite1 = Arr1->getElementType(); + Composite2 = Arr2->getElementType(); + Steps.emplace_back(Step::Array); + if (CAT1 || CAT2) + NeedConstBefore = Steps.size(); + continue; + } + } + const PointerType *Ptr1, *Ptr2; if ((Ptr1 = Composite1->getAs()) && (Ptr2 = Composite2->getAs())) { @@ -6771,8 +6801,6 @@ continue; } - // FIXME: arrays - // FIXME: block pointer types? // Cannot unwrap any more types. diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -3246,6 +3246,19 @@ !PreviousToQualsIncludeConst) return false; + // The following wording is from C++20, where the result of the conversion + // is T3, not T2. + // -- if [...] P1,i [...] is "array of unknown bound of", P3,i is + // "array of unknown bound of" + if (FromType->isIncompleteArrayType() && !ToType->isIncompleteArrayType()) + return false; + + // -- if the resulting P3,i is different from P1,i [...], then const is + // added to every cv 3_k for 0 < k < i. + if (!CStyle && FromType->isConstantArrayType() && + ToType->isIncompleteArrayType() && !PreviousToQualsIncludeConst) + return false; + // Keep track of whether all prior cv-qualifiers in the "to" type // include const. PreviousToQualsIncludeConst = @@ -4199,12 +4212,15 @@ CompareQualificationConversions(Sema &S, const StandardConversionSequence& SCS1, const StandardConversionSequence& SCS2) { - // C++ 13.3.3.2p3: + // C++ [over.ics.rank]p3: // -- S1 and S2 differ only in their qualification conversion and - // yield similar types T1 and T2 (C++ 4.4), respectively, and the - // cv-qualification signature of type T1 is a proper subset of - // the cv-qualification signature of type T2, and S1 is not the + // yield similar types T1 and T2 (C++ 4.4), respectively, [...] + // [C++98] + // [...] and the cv-qualification signature of type T1 is a proper subset + // of the cv-qualification signature of type T2, and S1 is not the // deprecated string literal array-to-pointer conversion (4.2). + // [C++2a] + // [...] where T1 can be converted to T2 by a qualification conversion. if (SCS1.First != SCS2.First || SCS1.Second != SCS2.Second || SCS1.Third != SCS2.Third || SCS1.Third != ICK_Qualification) return ImplicitConversionSequence::Indistinguishable; @@ -4225,79 +4241,35 @@ if (UnqualT1 == UnqualT2) return ImplicitConversionSequence::Indistinguishable; - ImplicitConversionSequence::CompareKind Result - = ImplicitConversionSequence::Indistinguishable; + // Don't ever prefer a standard conversion sequence that uses the deprecated + // string literal array to pointer conversion. + bool CanPick1 = !SCS1.DeprecatedStringLiteralToCharPtr; + bool CanPick2 = !SCS2.DeprecatedStringLiteralToCharPtr; // Objective-C++ ARC: // Prefer qualification conversions not involving a change in lifetime - // to qualification conversions that do not change lifetime. - if (SCS1.QualificationIncludesObjCLifetime != - SCS2.QualificationIncludesObjCLifetime) { - Result = SCS1.QualificationIncludesObjCLifetime - ? ImplicitConversionSequence::Worse - : ImplicitConversionSequence::Better; - } - - while (S.Context.UnwrapSimilarTypes(T1, T2)) { - // Within each iteration of the loop, we check the qualifiers to - // determine if this still looks like a qualification - // conversion. Then, if all is well, we unwrap one more level of - // pointers or pointers-to-members and do it all again - // until there are no more pointers or pointers-to-members left - // to unwrap. This essentially mimics what - // IsQualificationConversion does, but here we're checking for a - // strict subset of qualifiers. - if (T1.getQualifiers().withoutObjCLifetime() == - T2.getQualifiers().withoutObjCLifetime()) - // The qualifiers are the same, so this doesn't tell us anything - // about how the sequences rank. - // ObjC ownership quals are omitted above as they interfere with - // the ARC overload rule. - ; - else if (T2.isMoreQualifiedThan(T1)) { - // T1 has fewer qualifiers, so it could be the better sequence. - if (Result == ImplicitConversionSequence::Worse) - // Neither has qualifiers that are a subset of the other's - // qualifiers. - return ImplicitConversionSequence::Indistinguishable; - - Result = ImplicitConversionSequence::Better; - } else if (T1.isMoreQualifiedThan(T2)) { - // T2 has fewer qualifiers, so it could be the better sequence. - if (Result == ImplicitConversionSequence::Better) - // Neither has qualifiers that are a subset of the other's - // qualifiers. - return ImplicitConversionSequence::Indistinguishable; - - Result = ImplicitConversionSequence::Worse; - } else { - // Qualifiers are disjoint. - return ImplicitConversionSequence::Indistinguishable; - } - - // If the types after this point are equivalent, we're done. - if (S.Context.hasSameUnqualifiedType(T1, T2)) - break; - } - - // Check that the winning standard conversion sequence isn't using - // the deprecated string literal array to pointer conversion. - switch (Result) { - case ImplicitConversionSequence::Better: - if (SCS1.DeprecatedStringLiteralToCharPtr) - Result = ImplicitConversionSequence::Indistinguishable; - break; + // to qualification conversions that do change lifetime. + if (SCS1.QualificationIncludesObjCLifetime && + !SCS2.QualificationIncludesObjCLifetime) + CanPick1 = false; + if (SCS2.QualificationIncludesObjCLifetime && + !SCS1.QualificationIncludesObjCLifetime) + CanPick2 = false; - case ImplicitConversionSequence::Indistinguishable: - break; - - case ImplicitConversionSequence::Worse: - if (SCS2.DeprecatedStringLiteralToCharPtr) - Result = ImplicitConversionSequence::Indistinguishable; - break; - } - - return Result; + bool ObjCLifetimeConversion; + if (CanPick1 && + !S.IsQualificationConversion(T1, T2, false, ObjCLifetimeConversion)) + CanPick1 = false; + // FIXME: In Objective-C ARC, we can have qualification conversions in both + // directions, so we can't short-cut this second check in general. + if (CanPick2 && + !S.IsQualificationConversion(T2, T1, false, ObjCLifetimeConversion)) + CanPick2 = false; + + if (CanPick1 != CanPick2) + return CanPick1 ? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; + return ImplicitConversionSequence::Indistinguishable; } /// CompareDerivedToBaseConversions - Compares two standard conversion diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp --- a/clang/test/CXX/drs/dr3xx.cpp +++ b/clang/test/CXX/drs/dr3xx.cpp @@ -373,10 +373,19 @@ q = p; // ok q2 = p; // ok r = p; // expected-error {{incompatible}} - s = p; // expected-error {{incompatible}} (for now) + s = p; +#if __cplusplus < 202002 + // expected-error@-2 {{incompatible}} (fixed by p0388) +#endif t = p; // expected-error {{incompatible}} - s = q; // expected-error {{incompatible}} - s = q2; // expected-error {{incompatible}} + s = q; +#if __cplusplus < 202002 + // expected-error@-2 {{incompatible}} (fixed by p0388) +#endif + s = q2; +#if __cplusplus < 202002 + // expected-error@-2 {{incompatible}} (fixed by p0388) +#endif s = t; // ok, adding const t = s; // expected-error {{discards qualifiers}} (void) const_cast

(q); diff --git a/clang/test/CodeGenCXX/cxx20-p0388-unbound-ary.cpp b/clang/test/CodeGenCXX/cxx20-p0388-unbound-ary.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/cxx20-p0388-unbound-ary.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 %s -triple %itanium_abi_triple -std=c++20 -emit-llvm -O2 -o - | FileCheck %s + +// p0388 conversions to unbounded array +// dcl.init.list/3 + +namespace One { +int ga[1]; + +// CHECK-LABEL: @_ZN3One5frob1Ev +// CHECK-NEXT: entry: +// CHECK-NEXT: ret [0 x i32]* bitcast ([1 x i32]* @_ZN3One2gaE to [0 x i32]*) +auto &frob1() { + int(&r1)[] = ga; + + return r1; +} + +// CHECK-LABEL: @_ZN3One5frob2ERA1_i +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = bitcast [1 x i32]* %arp to [0 x i32]* +// CHECK-NEXT: ret [0 x i32]* %0 +auto &frob2(int (&arp)[1]) { + int(&r2)[] = arp; + + return r2; +} +} // namespace One diff --git a/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp b/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// RUN: %clang_cc1 -std=c++17 -verify %s + +// p0388 conversions to unbounded array +// dcl.init.list/3 + +namespace One { +int ga[1]; + +auto &frob1() { + int(&r1)[] = ga; +#if __cplusplus < 202002 + // expected-error@-2{{cannot bind to a value of unrelated type}} +#endif + + return r1; +} + +auto &frob2(int (&arp)[1]) { + int(&r2)[] = arp; +#if __cplusplus < 202002 + // expected-error@-2{{cannot bind to a value of unrelated type}} +#endif + + return r2; +} +} // namespace One + +namespace Two { +int ga[1]; + +auto *frob1() { + int(*r1)[] = &ga; +#if __cplusplus < 202002 + // expected-error@-2{{with an rvalue of type}} +#endif + + return r1; +} + +auto *frob2(int (*arp)[1]) { + int(*r2)[] = arp; +#if __cplusplus < 202002 + // expected-error@-2{{with an lvalue of type}} +#endif + + return r2; +} +} // namespace Two + +namespace Four { +using Inc = int[2]; +using Mat = Inc[1]; +Mat *ga[2]; + +auto *frob1() { + Inc(*const(*r1)[])[] = &ga; +#if __cplusplus < 202002 + // expected-error@-2{{with an rvalue of type}} +#else + // missing a required 'const' + Inc(*(*r2)[])[] = &ga; // expected-error{{cannot initialize}} +#endif + + return r1; +} + +auto *frob2(Mat *(*arp)[1]) { + Inc(*const(*r2)[])[] = arp; +#if __cplusplus < 202002 + // expected-error@-2{{with an lvalue of type}} +#else + Inc(*(*r3)[])[] = arp; // expected-error{{cannot initialize}} +#endif + + return r2; +} + +} // namespace Four