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 @@ -2367,8 +2367,10 @@ bool ObjCMethodsAreEqual(const ObjCMethodDecl *MethodDecl, const ObjCMethodDecl *MethodImp); - bool UnwrapSimilarTypes(QualType &T1, QualType &T2); - bool UnwrapSimilarArrayTypes(QualType &T1, QualType &T2); + bool UnwrapSimilarTypes(QualType &T1, QualType &T2, + bool AllowPiMismatch = true); + bool 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 @@ -5668,7 +5668,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]. -bool 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. +bool ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2, + bool AllowPiMismatch) { bool UnwrappedAny = false; while (true) { auto *AT1 = getAsArrayType(T1); @@ -5679,12 +5683,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().CPlusPlus2a && AllowPiMismatch && + isa(AT2))) return UnwrappedAny; - } else if (!isa(AT1) || - !isa(AT2)) { + } else if (isa(AT1)) { + if (!isa(AT2) && + !(getLangOpts().CPlusPlus2a && AllowPiMismatch && + isa(AT2))) + return UnwrappedAny; + } else { return UnwrappedAny; } @@ -5704,10 +5717,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(); @@ -5768,7 +5785,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 @@ -4593,10 +4593,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 @@ -2053,10 +2053,17 @@ } case CK_AtomicToNonAtomic: case CK_NonAtomicToAtomic: - case CK_NoOp: case CK_UserDefinedConversion: return Visit(const_cast(E)); + case CK_NoOp: { + // 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::Value *V = Visit(const_cast(E)); + return V ? Builder.CreateBitCast(V, ConvertType(DestTy)) : nullptr; + } + 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 @@ -1482,6 +1482,9 @@ argCK = CK_AnyPointerToBlockPointerCast; } else if (ivarRef.getType()->isPointerType()) { argCK = CK_BitCast; + } else if (argLoad.getType()->isAtomicType() && + !ivarRef.getType()->isAtomicType()) { + argCK = CK_AtomicToNonAtomic; } ImplicitCastExpr argCast(ImplicitCastExpr::OnStack, ivarRef.getType(), argCK, &argLoad, 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 @@ -1216,7 +1216,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 @@ -6557,6 +6557,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().CPlusPlus2a && (IAT1 != IAT2) && + ((bool)CAT1 != (bool)CAT2) && + (Steps.empty() || Steps.back().K != Step::Array))) { + // In C++2a 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())) { @@ -6617,8 +6647,6 @@ continue; } - // FIXME: arrays - // FIXME: block pointer types? // Cannot unwrap any more types. diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -8056,9 +8056,23 @@ if (S.CheckExceptionSpecCompatibility(CurInit.get(), DestType)) return ExprError(); + // C++2a [conv.init.list]p3: + // The type of the temporary is the type referenced by T, unless T is + // "reference to array of unknown bound of U" [...] + QualType TempType = Step->Type; + if (auto *IAT = S.Context.getAsIncompleteArrayType(Step->Type)) { + auto *CAT = S.Context.getAsConstantArrayType(CurInit.get()->getType()); + assert(CAT && "array temporary has unknown bound?"); + TempType = S.Context.getQualifiedType( + S.Context.getConstantArrayType(IAT->getElementType(), + CAT->getSize(), nullptr, + ArrayType::Normal, 0), + Step->Type.getQualifiers()); + } + // Materialize the temporary into memory. MaterializeTemporaryExpr *MTE = S.CreateMaterializeTemporaryExpr( - Step->Type, CurInit.get(), Entity.getType()->isLValueReferenceType()); + TempType, CurInit.get(), Entity.getType()->isLValueReferenceType()); CurInit = MTE; // If we're extending this temporary to automatic storage duration -- we 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 @@ -3243,6 +3243,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 = @@ -4128,12 +4141,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; @@ -4154,79 +4170,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; - - case ImplicitConversionSequence::Indistinguishable: - break; - - case ImplicitConversionSequence::Worse: - if (SCS2.DeprecatedStringLiteralToCharPtr) - Result = ImplicitConversionSequence::Indistinguishable; - break; - } - - return Result; + // to qualification conversions that do change lifetime. + if (SCS1.QualificationIncludesObjCLifetime && + !SCS2.QualificationIncludesObjCLifetime) + CanPick1 = false; + if (SCS2.QualificationIncludesObjCLifetime && + !SCS1.QualificationIncludesObjCLifetime) + CanPick2 = false; + + 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