Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2491,8 +2491,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 Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -5783,7 +5783,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) @@ -5795,12 +5799,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 unbound array type. + // FIXME: Consider unwrapping VLAs. 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; } @@ -5819,10 +5832,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(); @@ -5883,7 +5900,7 @@ if (hasSameType(T1, T2)) return true; - if (!UnwrapSimilarTypes(T1, T2)) + if (!UnwrapSimilarTypes(T1, T2, /*AllowPiMismatch*/ false)) return false; } } Index: clang/lib/CodeGen/CGExpr.cpp =================================================================== --- clang/lib/CodeGen/CGExpr.cpp +++ clang/lib/CodeGen/CGExpr.cpp @@ -4684,10 +4684,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 = Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/lib/CodeGen/CGExprScalar.cpp @@ -2140,10 +2140,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!"); Index: clang/lib/CodeGen/CGObjC.cpp =================================================================== --- clang/lib/CodeGen/CGObjC.cpp +++ clang/lib/CodeGen/CGObjC.cpp @@ -1554,6 +1554,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()); Index: clang/lib/Sema/SemaCast.cpp =================================================================== --- clang/lib/Sema/SemaCast.cpp +++ clang/lib/Sema/SemaCast.cpp @@ -1304,7 +1304,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. Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -6610,11 +6610,10 @@ case ObjCPointer: return Ctx.getObjCObjectPointerType(T); case Array: - if (auto *CAT = cast_or_null(ClassOrBound)) + if (const auto *CAT = cast_or_null(ClassOrBound)) return Ctx.getConstantArrayType(T, CAT->getSize(), nullptr, ArrayType::Normal, 0); - else - return Ctx.getIncompleteArrayType(T, ArrayType::Normal, 0); + return Ctx.getIncompleteArrayType(T, ArrayType::Normal, 0); } llvm_unreachable("unknown step kind"); } @@ -6690,6 +6689,50 @@ } // FIXME: Can we unify the following with UnwrapSimilarTypes? + + const ArrayType *Arr1, *Arr2; + if ((Arr1 = Context.getAsArrayType(Composite1)) && + (Arr2 = Context.getAsArrayType(Composite2))) { + bool Unify = false; + auto *CAT1 = dyn_cast(Arr1); + auto *CAT2 = dyn_cast(Arr2); + if (CAT1 && CAT2 && CAT1->getSize() == CAT2->getSize()) + Unify = true; + else { + // All remaining available conversions will be to unbound array. + CAT1 = CAT2 = nullptr; + + if (isa(Arr1) && isa(Arr2)) + Unify = true; + else if (isa(Arr1) || isa(Arr2)) + // We don't handle VLA->Unbound decay as a standard conversion. + // These must be the same type, or failure. + // FIXME: When ICS understands VLA->Unbound we should allow such + // decay here. If the two types are the same, we should exit the + // loop, because we cannot recompose a VLA type. + break; + else if (getLangOpts().CPlusPlus20 && + (Steps.empty() || Steps.back().K != Step::Array)) { + // C++20 allows unifying arrays of different bound to form an + // unbounded array but not to form an array with unbounded element + // type. + Unify = true; + if (!Steps.empty()) + NeedConstBefore = Steps.size() - 1; + } + } + + if (Unify) { + Composite1 = Arr1->getElementType(); + Composite2 = Arr2->getElementType(); + Steps.emplace_back(Step::Array, CAT1); + continue; + } + + // There are no other ways of unifying array types + break; + } + const PointerType *Ptr1, *Ptr2; if ((Ptr1 = Composite1->getAs()) && (Ptr2 = Composite2->getAs())) { @@ -6750,8 +6793,6 @@ continue; } - // FIXME: arrays - // FIXME: block pointer types? // Cannot unwrap any more types. Index: clang/lib/Sema/SemaInit.cpp =================================================================== --- clang/lib/Sema/SemaInit.cpp +++ clang/lib/Sema/SemaInit.cpp @@ -8252,7 +8252,7 @@ // When this is an incomplete array type (such as when this is // initializing an array of unknown bounds from an init list), use THAT - // type instead so that we propogate the array bounds. + // type instead so that we propagate the array bounds. if (MTETy->isIncompleteArrayType() && !CurInit.get()->getType()->isIncompleteArrayType() && S.Context.hasSameType( Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -3244,6 +3244,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 = @@ -4197,12 +4210,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; @@ -4223,79 +4239,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 Index: clang/test/CXX/drs/dr3xx.cpp =================================================================== --- clang/test/CXX/drs/dr3xx.cpp +++ 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); Index: clang/test/CodeGenCXX/cxx20-p0388-unbound-ary.cpp =================================================================== --- /dev/null +++ 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 Index: clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp @@ -0,0 +1,160 @@ +// 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 + +template +struct Same { + static constexpr bool val = false; +}; +template +struct Same { + static constexpr bool val = true; +}; + +namespace Five { +// common pointer type for differently sized arrays + +void foo(bool c) { + int(*a1)[3]; + int(*b1)[4]; + auto *p = c ? a1 : b1; +#if __cplusplus < 202002 + // expected-error@-2{{incompatible operand types}} +#else + static_assert(Same::val); +#endif +} + +void bar(bool c) { + int a2[3][4]; + int b2[4][4]; + auto *p = c ? &a2 : &b2; +#if __cplusplus < 202002 + // expected-error@-2{{incompatible operand types}} +#else + static_assert(Same::val); +#endif +} + +void baz(bool c) { + int(*a3)[3][4]; + int(*b3)[4][4]; + auto *p = c ? &a3 : &b3; +#if __cplusplus < 202002 + // expected-error@-2{{incompatible operand types}} +#else + static_assert(Same::val); +#endif +} + +void quux(bool c) { + int a4[4][3]; + int b4[4][4]; + auto *p = c ? &a4 : &b4; // expected-error{{incompatible operand types}} +} + +} // namespace Five + +namespace Six { +// Common pointer type for involving VLAs. +// We don't handle these as best we might -- not decaying to unbound array for +// instance. + +void foo(bool c, int s) { + int a[4], b[s]; + auto *p = c ? &a : &b; // expected-error{{incompatible operand types}} +} + +void bar(bool c, int s) { + using v_t = int[s]; + v_t a, b; // same type + auto *p = c ? &a : &b; +} + +void baz(bool c, int s) { + int a[s], b[s]; // NOT the same type! + auto *p = c ? &a : &b; // expected-error{{incompatible operand types}} +} + +void quux(bool c, int s) { + int a[s], b[4]; + auto *p = c ? &a : &b; // expected-error{{incompatible operand types}} +} + +} // namespace Six