From b9d678d22f74ebd6e34f0a3501fb01d3d80984e7 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Fri, 15 Nov 2024 06:58:36 -0600 Subject: [PATCH] [Clang] Use TargetInfo when deciding if an address space is compatible (#115777) Summary: Address spaces are used in several embedded and GPU targets to describe accesses to different types of memory. Currently we use the address space enumerations to control which address spaces are considered supersets of eachother, however this is also a target level property as described by the C standard's passing mentions. This patch allows the address space checks to use the target information to decide if a pointer conversion is legal. For AMDGPU and NVPTX, all supported address spaces can be converted to the default address space. More semantic checks can be added on top of this, for now I'm mainly looking to get more standard semantics working for C/C++. Right now the address space conversions must all be done explicitly in C/C++ unlike the offloading languages which define their own custom address spaces that just map to the same target specific ones anyway. The main question is if this behavior is a function of the target or the language. --- .../bugprone/VirtualNearMissCheck.cpp | 2 +- .../SuspiciousCallArgumentCheck.cpp | 33 +++-- clang/include/clang/AST/CanonicalType.h | 9 +- clang/include/clang/AST/Type.h | 61 +++----- clang/include/clang/Basic/TargetInfo.h | 7 + clang/lib/AST/ASTContext.cpp | 2 +- clang/lib/AST/Type.cpp | 30 ++++ clang/lib/Basic/Targets/AMDGPU.h | 12 ++ clang/lib/Basic/Targets/NVPTX.h | 15 ++ clang/lib/Sema/SemaARM.cpp | 2 +- clang/lib/Sema/SemaCast.cpp | 19 ++- clang/lib/Sema/SemaCodeComplete.cpp | 10 +- clang/lib/Sema/SemaDeclCXX.cpp | 2 +- clang/lib/Sema/SemaExceptionSpec.cpp | 6 +- clang/lib/Sema/SemaExpr.cpp | 24 +-- clang/lib/Sema/SemaExprCXX.cpp | 9 +- clang/lib/Sema/SemaFixItUtils.cpp | 6 +- clang/lib/Sema/SemaInit.cpp | 19 +-- clang/lib/Sema/SemaObjC.cpp | 2 +- clang/lib/Sema/SemaOpenMP.cpp | 8 +- clang/lib/Sema/SemaOverload.cpp | 48 +++--- clang/lib/Sema/SemaTemplateDeduction.cpp | 5 +- clang/test/CodeGen/target-addrspace.cpp | 140 ++++++++++++++++++ clang/test/Sema/amdgcn-address-spaces.c | 20 +++ clang/test/Sema/nvptx-address-spaces.c | 21 +++ 25 files changed, 383 insertions(+), 129 deletions(-) create mode 100644 clang/test/CodeGen/target-addrspace.cpp create mode 100644 clang/test/Sema/amdgcn-address-spaces.c create mode 100644 clang/test/Sema/nvptx-address-spaces.c diff --git a/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp index bebdce525e28..76fa2d916f0e 100644 --- a/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp @@ -112,7 +112,7 @@ static bool checkOverridingFunctionReturnType(const ASTContext *Context, // The class type D should have the same cv-qualification as or less // cv-qualification than the class type B. - if (DTy.isMoreQualifiedThan(BTy)) + if (DTy.isMoreQualifiedThan(BTy, *Context)) return false; return true; diff --git a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp index 18420d0c8488..c5eaff88e0ed 100644 --- a/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SuspiciousCallArgumentCheck.cpp @@ -299,10 +299,11 @@ static bool applyDiceHeuristic(StringRef Arg, StringRef Param, /// Checks if ArgType binds to ParamType regarding reference-ness and /// cv-qualifiers. -static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType) { +static bool areRefAndQualCompatible(QualType ArgType, QualType ParamType, + const ASTContext &Ctx) { return !ParamType->isReferenceType() || ParamType.getNonReferenceType().isAtLeastAsQualifiedAs( - ArgType.getNonReferenceType()); + ArgType.getNonReferenceType(), Ctx); } static bool isPointerOrArray(QualType TypeToCheck) { @@ -311,12 +312,12 @@ static bool isPointerOrArray(QualType TypeToCheck) { /// Checks whether ArgType is an array type identical to ParamType's array type. /// Enforces array elements' qualifier compatibility as well. -static bool isCompatibleWithArrayReference(QualType ArgType, - QualType ParamType) { +static bool isCompatibleWithArrayReference(QualType ArgType, QualType ParamType, + const ASTContext &Ctx) { if (!ArgType->isArrayType()) return false; // Here, qualifiers belong to the elements of the arrays. - if (!ParamType.isAtLeastAsQualifiedAs(ArgType)) + if (!ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx)) return false; return ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType(); @@ -342,12 +343,13 @@ static QualType convertToPointeeOrArrayElementQualType(QualType TypeToConvert) { /// every * in ParamType to the right of that cv-qualifier, except the last /// one, must also be const-qualified. static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType, - bool &IsParamContinuouslyConst) { + bool &IsParamContinuouslyConst, + const ASTContext &Ctx) { // The types are compatible, if the parameter is at least as qualified as the // argument, and if it is more qualified, it has to be const on upper pointer // levels. bool AreTypesQualCompatible = - ParamType.isAtLeastAsQualifiedAs(ArgType) && + ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx) && (!ParamType.hasQualifiers() || IsParamContinuouslyConst); // Check whether the parameter's constness continues at the current pointer // level. @@ -359,9 +361,10 @@ static bool arePointersStillQualCompatible(QualType ArgType, QualType ParamType, /// Checks whether multilevel pointers are compatible in terms of levels, /// qualifiers and pointee type. static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType, - bool IsParamContinuouslyConst) { + bool IsParamContinuouslyConst, + const ASTContext &Ctx) { if (!arePointersStillQualCompatible(ArgType, ParamType, - IsParamContinuouslyConst)) + IsParamContinuouslyConst, Ctx)) return false; do { @@ -372,7 +375,7 @@ static bool arePointerTypesCompatible(QualType ArgType, QualType ParamType, // Check whether cv-qualifiers permit compatibility on // current level. if (!arePointersStillQualCompatible(ArgType, ParamType, - IsParamContinuouslyConst)) + IsParamContinuouslyConst, Ctx)) return false; if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType()) @@ -396,7 +399,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType, return true; // Check for constness and reference compatibility. - if (!areRefAndQualCompatible(ArgType, ParamType)) + if (!areRefAndQualCompatible(ArgType, ParamType, Ctx)) return false; bool IsParamReference = ParamType->isReferenceType(); @@ -434,7 +437,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType, // When ParamType is an array reference, ArgType has to be of the same-sized // array-type with cv-compatible element type. if (IsParamReference && ParamType->isArrayType()) - return isCompatibleWithArrayReference(ArgType, ParamType); + return isCompatibleWithArrayReference(ArgType, ParamType, Ctx); bool IsParamContinuouslyConst = !IsParamReference || ParamType.getNonReferenceType().isConstQualified(); @@ -444,7 +447,7 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType, ParamType = convertToPointeeOrArrayElementQualType(ParamType); // Check qualifier compatibility on the next level. - if (!ParamType.isAtLeastAsQualifiedAs(ArgType)) + if (!ParamType.isAtLeastAsQualifiedAs(ArgType, Ctx)) return false; if (ParamType.getUnqualifiedType() == ArgType.getUnqualifiedType()) @@ -472,8 +475,8 @@ static bool areTypesCompatible(QualType ArgType, QualType ParamType, if (!(ParamType->isAnyPointerType() && ArgType->isAnyPointerType())) return false; - return arePointerTypesCompatible(ArgType, ParamType, - IsParamContinuouslyConst); + return arePointerTypesCompatible(ArgType, ParamType, IsParamContinuouslyConst, + Ctx); } static bool isOverloadedUnaryOrBinarySymbolOperator(const FunctionDecl *FD) { diff --git a/clang/include/clang/AST/CanonicalType.h b/clang/include/clang/AST/CanonicalType.h index 6102eb017935..6699284d215b 100644 --- a/clang/include/clang/AST/CanonicalType.h +++ b/clang/include/clang/AST/CanonicalType.h @@ -30,6 +30,7 @@ namespace clang { template class CanProxy; template struct CanProxyAdaptor; +class ASTContext; class CXXRecordDecl; class EnumDecl; class Expr; @@ -164,14 +165,14 @@ public: /// Determines whether this canonical type is more qualified than /// the @p Other canonical type. - bool isMoreQualifiedThan(CanQual Other) const { - return Stored.isMoreQualifiedThan(Other.Stored); + bool isMoreQualifiedThan(CanQual Other, const ASTContext &Ctx) const { + return Stored.isMoreQualifiedThan(Other.Stored, Ctx); } /// Determines whether this canonical type is at least as qualified as /// the @p Other canonical type. - bool isAtLeastAsQualifiedAs(CanQual Other) const { - return Stored.isAtLeastAsQualifiedAs(Other.Stored); + bool isAtLeastAsQualifiedAs(CanQual Other, const ASTContext &Ctx) const { + return Stored.isAtLeastAsQualifiedAs(Other.Stored, Ctx); } /// If the canonical type is a reference type, returns the type that diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 897912901716..42c7e983dc9e 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -31,6 +31,7 @@ #include "clang/Basic/PointerAuthOptions.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Basic/Visibility.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/APSInt.h" @@ -323,6 +324,7 @@ public: /// * Objective C: the GC attributes (none, weak, or strong) class Qualifiers { public: + Qualifiers() = default; enum TQ : uint64_t { // NOTE: These flags must be kept in sync with DeclSpec::TQ. Const = 0x1, @@ -697,45 +699,27 @@ public: /// every address space is a superset of itself. /// CL2.0 adds: /// __generic is a superset of any address space except for __constant. - static bool isAddressSpaceSupersetOf(LangAS A, LangAS B) { + static bool isAddressSpaceSupersetOf(LangAS A, LangAS B, + const ASTContext &Ctx) { // Address spaces must match exactly. - return A == B || - // Otherwise in OpenCLC v2.0 s6.5.5: every address space except - // for __constant can be used as __generic. - (A == LangAS::opencl_generic && B != LangAS::opencl_constant) || - // We also define global_device and global_host address spaces, - // to distinguish global pointers allocated on host from pointers - // allocated on device, which are a subset of __global. - (A == LangAS::opencl_global && (B == LangAS::opencl_global_device || - B == LangAS::opencl_global_host)) || - (A == LangAS::sycl_global && (B == LangAS::sycl_global_device || - B == LangAS::sycl_global_host)) || - // Consider pointer size address spaces to be equivalent to default. - ((isPtrSizeAddressSpace(A) || A == LangAS::Default) && - (isPtrSizeAddressSpace(B) || B == LangAS::Default)) || - // Default is a superset of SYCL address spaces. - (A == LangAS::Default && - (B == LangAS::sycl_private || B == LangAS::sycl_local || - B == LangAS::sycl_global || B == LangAS::sycl_global_device || - B == LangAS::sycl_global_host)) || - // In HIP device compilation, any cuda address space is allowed - // to implicitly cast into the default address space. - (A == LangAS::Default && - (B == LangAS::cuda_constant || B == LangAS::cuda_device || - B == LangAS::cuda_shared)); + return A == B || isTargetAddressSpaceSupersetOf(A, B, Ctx); } + static bool isTargetAddressSpaceSupersetOf(LangAS A, LangAS B, + const ASTContext &Ctx); + /// Returns true if the address space in these qualifiers is equal to or /// a superset of the address space in the argument qualifiers. - bool isAddressSpaceSupersetOf(Qualifiers other) const { - return isAddressSpaceSupersetOf(getAddressSpace(), other.getAddressSpace()); + bool isAddressSpaceSupersetOf(Qualifiers other, const ASTContext &Ctx) const { + return isAddressSpaceSupersetOf(getAddressSpace(), other.getAddressSpace(), + Ctx); } /// Determines if these qualifiers compatibly include another set. /// Generally this answers the question of whether an object with the other /// qualifiers can be safely used as an object with these qualifiers. - bool compatiblyIncludes(Qualifiers other) const { - return isAddressSpaceSupersetOf(other) && + bool compatiblyIncludes(Qualifiers other, const ASTContext &Ctx) const { + return isAddressSpaceSupersetOf(other, Ctx) && // ObjC GC qualifiers can match, be added, or be removed, but can't // be changed. (getObjCGCAttr() == other.getObjCGCAttr() || !hasObjCGCAttr() || @@ -1273,11 +1257,11 @@ public: /// Determine whether this type is more qualified than the other /// given type, requiring exact equality for non-CVR qualifiers. - bool isMoreQualifiedThan(QualType Other) const; + bool isMoreQualifiedThan(QualType Other, const ASTContext &Ctx) const; /// Determine whether this type is at least as qualified as the other /// given type, requiring exact equality for non-CVR qualifiers. - bool isAtLeastAsQualifiedAs(QualType Other) const; + bool isAtLeastAsQualifiedAs(QualType Other, const ASTContext &Ctx) const; QualType getNonReferenceType() const; @@ -1425,11 +1409,12 @@ public: /// address spaces overlap iff they are they same. /// OpenCL C v2.0 s6.5.5 adds: /// __generic overlaps with any address space except for __constant. - bool isAddressSpaceOverlapping(QualType T) const { + bool isAddressSpaceOverlapping(QualType T, const ASTContext &Ctx) const { Qualifiers Q = getQualifiers(); Qualifiers TQ = T.getQualifiers(); // Address spaces overlap if at least one of them is a superset of another - return Q.isAddressSpaceSupersetOf(TQ) || TQ.isAddressSpaceSupersetOf(Q); + return Q.isAddressSpaceSupersetOf(TQ, Ctx) || + TQ.isAddressSpaceSupersetOf(Q, Ctx); } /// Returns gc attribute of this type. @@ -8112,24 +8097,26 @@ inline FunctionType::ExtInfo getFunctionExtInfo(QualType t) { /// is more qualified than "const int", "volatile int", and /// "int". However, it is not more qualified than "const volatile /// int". -inline bool QualType::isMoreQualifiedThan(QualType other) const { +inline bool QualType::isMoreQualifiedThan(QualType other, + const ASTContext &Ctx) const { Qualifiers MyQuals = getQualifiers(); Qualifiers OtherQuals = other.getQualifiers(); - return (MyQuals != OtherQuals && MyQuals.compatiblyIncludes(OtherQuals)); + return (MyQuals != OtherQuals && MyQuals.compatiblyIncludes(OtherQuals, Ctx)); } /// Determine whether this type is at last /// as qualified as the Other type. For example, "const volatile /// int" is at least as qualified as "const int", "volatile int", /// "int", and "const volatile int". -inline bool QualType::isAtLeastAsQualifiedAs(QualType other) const { +inline bool QualType::isAtLeastAsQualifiedAs(QualType other, + const ASTContext &Ctx) const { Qualifiers OtherQuals = other.getQualifiers(); // Ignore __unaligned qualifier if this type is a void. if (getUnqualifiedType()->isVoidType()) OtherQuals.removeUnaligned(); - return getQualifiers().compatiblyIncludes(OtherQuals); + return getQualifiers().compatiblyIncludes(OtherQuals, Ctx); } /// If Type is a reference type (e.g., const diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 2b3552854e1f..019ca25bb57f 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -486,6 +486,13 @@ public: /// \param AddrSpace address space of pointee in source language. virtual uint64_t getNullPointerValue(LangAS AddrSpace) const { return 0; } + /// Returns true if an address space can be safely converted to another. + /// \param A address space of target in source language. + /// \param B address space of source in source language. + virtual bool isAddressSpaceSupersetOf(LangAS A, LangAS B) const { + return A == B; + } + /// Return the size of '_Bool' and C++ 'bool' for this target, in bits. unsigned getBoolWidth() const { return BoolWidth; } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 232081bdb46c..5226ca6f5d01 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -11405,7 +11405,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, Qualifiers RHSPteeQual = RHSPointee.getQualifiers(); // Blocks can't be an expression in a ternary operator (OpenCL v2.0 // 6.12.5) thus the following check is asymmetric. - if (!LHSPteeQual.isAddressSpaceSupersetOf(RHSPteeQual)) + if (!LHSPteeQual.isAddressSpaceSupersetOf(RHSPteeQual, *this)) return {}; LHSPteeQual.removeAddressSpace(); RHSPteeQual.removeAddressSpace(); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 7de13977176f..7ecb986e4dc2 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -73,6 +73,36 @@ bool Qualifiers::isStrictSupersetOf(Qualifiers Other) const { (hasObjCLifetime() && !Other.hasObjCLifetime())); } +bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B, + const ASTContext &Ctx) { + // In OpenCLC v2.0 s6.5.5: every address space except for __constant can be + // used as __generic. + return (A == LangAS::opencl_generic && B != LangAS::opencl_constant) || + // We also define global_device and global_host address spaces, + // to distinguish global pointers allocated on host from pointers + // allocated on device, which are a subset of __global. + (A == LangAS::opencl_global && (B == LangAS::opencl_global_device || + B == LangAS::opencl_global_host)) || + (A == LangAS::sycl_global && + (B == LangAS::sycl_global_device || B == LangAS::sycl_global_host)) || + // Consider pointer size address spaces to be equivalent to default. + ((isPtrSizeAddressSpace(A) || A == LangAS::Default) && + (isPtrSizeAddressSpace(B) || B == LangAS::Default)) || + // Default is a superset of SYCL address spaces. + (A == LangAS::Default && + (B == LangAS::sycl_private || B == LangAS::sycl_local || + B == LangAS::sycl_global || B == LangAS::sycl_global_device || + B == LangAS::sycl_global_host)) || + // In HIP device compilation, any cuda address space is allowed + // to implicitly cast into the default address space. + (A == LangAS::Default && + (B == LangAS::cuda_constant || B == LangAS::cuda_device || + B == LangAS::cuda_shared)) || + // Conversions from target specific address spaces may be legal + // depending on the target information. + Ctx.getTargetInfo().isAddressSpaceSupersetOf(A, B); +} + const IdentifierInfo* QualType::getBaseTypeIdentifier() const { const Type* ty = getTypePtr(); NamedDecl *ND = nullptr; diff --git a/clang/lib/Basic/Targets/AMDGPU.h b/clang/lib/Basic/Targets/AMDGPU.h index fac46f215a37..db7a095ba2a4 100644 --- a/clang/lib/Basic/Targets/AMDGPU.h +++ b/clang/lib/Basic/Targets/AMDGPU.h @@ -111,6 +111,18 @@ public: return getPointerWidthV(AddrSpace); } + virtual bool isAddressSpaceSupersetOf(LangAS A, LangAS B) const override { + // The flat address space AS(0) is a superset of all the other address + // spaces used by the backend target. + return A == B || + ((A == LangAS::Default || + (isTargetAddressSpace(A) && + toTargetAddressSpace(A) == llvm::AMDGPUAS::FLAT_ADDRESS)) && + isTargetAddressSpace(B) && + toTargetAddressSpace(B) >= llvm::AMDGPUAS::FLAT_ADDRESS && + toTargetAddressSpace(B) <= llvm::AMDGPUAS::PRIVATE_ADDRESS); + } + uint64_t getMaxPointerWidth() const override { return getTriple().getArch() == llvm::Triple::amdgcn ? 64 : 32; } diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h index 165b28a60fb2..d81b89a7f24a 100644 --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -17,6 +17,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/NVPTXAddrSpace.h" #include "llvm/TargetParser/Triple.h" #include @@ -89,6 +90,20 @@ public: bool hasFeature(StringRef Feature) const override; + virtual bool isAddressSpaceSupersetOf(LangAS A, LangAS B) const override { + // The generic address space AS(0) is a superset of all the other address + // spaces used by the backend target. + return A == B || + ((A == LangAS::Default || + (isTargetAddressSpace(A) && + toTargetAddressSpace(A) == + llvm::NVPTXAS::ADDRESS_SPACE_GENERIC)) && + isTargetAddressSpace(B) && + toTargetAddressSpace(B) >= llvm::NVPTXAS::ADDRESS_SPACE_GENERIC && + toTargetAddressSpace(B) <= llvm::NVPTXAS::ADDRESS_SPACE_LOCAL && + toTargetAddressSpace(B) != 2); + } + ArrayRef getGCCRegNames() const override; ArrayRef getGCCRegAliases() const override { diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp index c3a6e5ef8a9d..3e93b38143f3 100644 --- a/clang/lib/Sema/SemaARM.cpp +++ b/clang/lib/Sema/SemaARM.cpp @@ -907,7 +907,7 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(unsigned BuiltinID, // Issue a warning if the cast is dodgy. CastKind CastNeeded = CK_NoOp; - if (!AddrType.isAtLeastAsQualifiedAs(ValType)) { + if (!AddrType.isAtLeastAsQualifiedAs(ValType, getASTContext())) { CastNeeded = CK_BitCast; Diag(DRE->getBeginLoc(), diag::ext_typecheck_convert_discards_qualifiers) << PointerArg->getType() << Context.getPointerType(AddrType) diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 3eb013e3612d..cfb82dcc4df4 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -731,7 +731,8 @@ CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType, *CastAwayQualifiers = SrcCvrQuals - DestCvrQuals; // If we removed a cvr-qualifier, this is casting away 'constness'. - if (!DestCvrQuals.compatiblyIncludes(SrcCvrQuals)) { + if (!DestCvrQuals.compatiblyIncludes(SrcCvrQuals, + Self.getASTContext())) { if (TheOffendingSrcType) *TheOffendingSrcType = PrevUnwrappedSrcType; if (TheOffendingDestType) @@ -889,7 +890,7 @@ void CastOperation::CheckDynamicCast() { assert(SrcRecord && "Bad source pointee slipped through."); // C++ 5.2.7p1: The dynamic_cast operator shall not cast away constness. - if (!DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) { + if (!DestPointee.isAtLeastAsQualifiedAs(SrcPointee, Self.getASTContext())) { Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_qualifiers_away) << CT_Dynamic << OrigSrcType << this->DestType << OpRange; SrcExpr = ExprError(); @@ -1463,7 +1464,8 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, SrcPointeeQuals.removeObjCGCAttr(); SrcPointeeQuals.removeObjCLifetime(); if (DestPointeeQuals != SrcPointeeQuals && - !DestPointeeQuals.compatiblyIncludes(SrcPointeeQuals)) { + !DestPointeeQuals.compatiblyIncludes(SrcPointeeQuals, + Self.getASTContext())) { msg = diag::err_bad_cxx_cast_qualifiers_away; return TC_Failed; } @@ -1695,7 +1697,8 @@ TryStaticDowncast(Sema &Self, CanQualType SrcType, CanQualType DestType, // FIXME: Being 100% compliant here would be nice to have. // Must preserve cv, as always, unless we're in C-style mode. - if (!CStyle && !DestType.isAtLeastAsQualifiedAs(SrcType)) { + if (!CStyle && + !DestType.isAtLeastAsQualifiedAs(SrcType, Self.getASTContext())) { msg = diag::err_bad_cxx_cast_qualifiers_away; return TC_Failed; } @@ -2524,7 +2527,7 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, assert(SrcType->isPointerType() && DestType->isPointerType()); if (!CStyle && !DestType->getPointeeType().getQualifiers().isAddressSpaceSupersetOf( - SrcType->getPointeeType().getQualifiers())) { + SrcType->getPointeeType().getQualifiers(), Self.getASTContext())) { SuccessResult = TC_Failed; } } else if (IsLValueCast) { @@ -2631,7 +2634,8 @@ static TryCastResult TryAddressSpaceCast(Sema &Self, ExprResult &SrcExpr, return TC_NotApplicable; auto SrcPointeeType = SrcPtrType->getPointeeType(); auto DestPointeeType = DestPtrType->getPointeeType(); - if (!DestPointeeType.isAddressSpaceOverlapping(SrcPointeeType)) { + if (!DestPointeeType.isAddressSpaceOverlapping(SrcPointeeType, + Self.getASTContext())) { msg = diag::err_bad_cxx_cast_addr_space_mismatch; return TC_Failed; } @@ -2676,7 +2680,8 @@ void CastOperation::checkAddressSpaceCast(QualType SrcType, QualType DestType) { QualType SrcPPointee = SrcPPtr->getPointeeType(); if (Nested ? DestPPointee.getAddressSpace() != SrcPPointee.getAddressSpace() - : !DestPPointee.isAddressSpaceOverlapping(SrcPPointee)) { + : !DestPPointee.isAddressSpaceOverlapping(SrcPPointee, + Self.getASTContext())) { Self.Diag(OpRange.getBegin(), DiagID) << SrcType << DestType << AssignmentAction::Casting << SrcExpr.get()->getSourceRange(); diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 45fc8a6df52d..890ca96790ac 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1246,7 +1246,8 @@ enum class OverloadCompare { BothViable, Dominates, Dominated }; static OverloadCompare compareOverloads(const CXXMethodDecl &Candidate, const CXXMethodDecl &Incumbent, const Qualifiers &ObjectQuals, - ExprValueKind ObjectKind) { + ExprValueKind ObjectKind, + const ASTContext &Ctx) { // Base/derived shadowing is handled elsewhere. if (Candidate.getDeclContext() != Incumbent.getDeclContext()) return OverloadCompare::BothViable; @@ -1280,8 +1281,8 @@ static OverloadCompare compareOverloads(const CXXMethodDecl &Candidate, // So make some decision based on the qualifiers. Qualifiers CandidateQual = Candidate.getMethodQualifiers(); Qualifiers IncumbentQual = Incumbent.getMethodQualifiers(); - bool CandidateSuperset = CandidateQual.compatiblyIncludes(IncumbentQual); - bool IncumbentSuperset = IncumbentQual.compatiblyIncludes(CandidateQual); + bool CandidateSuperset = CandidateQual.compatiblyIncludes(IncumbentQual, Ctx); + bool IncumbentSuperset = IncumbentQual.compatiblyIncludes(CandidateQual, Ctx); if (CandidateSuperset == IncumbentSuperset) return OverloadCompare::BothViable; return IncumbentSuperset ? OverloadCompare::Dominates @@ -1452,7 +1453,8 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext, Result &Incumbent = Results[Entry.second]; switch (compareOverloads(*Method, *cast(Incumbent.Declaration), - ObjectTypeQualifiers, ObjectKind)) { + ObjectTypeQualifiers, ObjectKind, + CurContext->getParentASTContext())) { case OverloadCompare::Dominates: // Replace the dominated overload with this one. // FIXME: if the overload dominates multiple incumbents then we diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 5d81d6db5b0e..a081602e28aa 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18329,7 +18329,7 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New, // The new class type must have the same or less qualifiers as the old type. - if (!OldClassTy.isAtLeastAsQualifiedAs(NewClassTy)) { + if (!OldClassTy.isAtLeastAsQualifiedAs(NewClassTy, getASTContext())) { Diag(New->getLocation(), diag::err_covariant_return_type_class_type_not_same_or_less_qualified) << New->getDeclName() << NewTy << OldTy diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 0d5ab0b3cd09..bfcdab91dd6f 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -717,9 +717,9 @@ bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) { Qualifiers EQuals, HQuals; ExceptionType = Context.getUnqualifiedArrayType( ExceptionType->getPointeeType(), EQuals); - HandlerType = Context.getUnqualifiedArrayType( - HandlerType->getPointeeType(), HQuals); - if (!HQuals.compatiblyIncludes(EQuals)) + HandlerType = + Context.getUnqualifiedArrayType(HandlerType->getPointeeType(), HQuals); + if (!HQuals.compatiblyIncludes(EQuals, getASTContext())) return false; if (HandlerType->isVoidType() && ExceptionType->isObjectType()) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index c9de7811adfe..e142373b1ab4 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -8026,9 +8026,9 @@ static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, // OpenCL v1.1 s6.5 - Conversion between pointers to distinct address // spaces is disallowed. - if (lhQual.isAddressSpaceSupersetOf(rhQual)) + if (lhQual.isAddressSpaceSupersetOf(rhQual, S.getASTContext())) ResultAddrSpace = LAddrSpace; - else if (rhQual.isAddressSpaceSupersetOf(lhQual)) + else if (rhQual.isAddressSpaceSupersetOf(lhQual, S.getASTContext())) ResultAddrSpace = RAddrSpace; else { S.Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) @@ -8940,17 +8940,17 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType, rhq.removeObjCLifetime(); } - if (!lhq.compatiblyIncludes(rhq)) { + if (!lhq.compatiblyIncludes(rhq, S.getASTContext())) { // Treat address-space mismatches as fatal. - if (!lhq.isAddressSpaceSupersetOf(rhq)) + if (!lhq.isAddressSpaceSupersetOf(rhq, S.getASTContext())) return Sema::IncompatiblePointerDiscardsQualifiers; // It's okay to add or remove GC or lifetime qualifiers when converting to // and from void*. - else if (lhq.withoutObjCGCAttr().withoutObjCLifetime() - .compatiblyIncludes( - rhq.withoutObjCGCAttr().withoutObjCLifetime()) - && (lhptee->isVoidType() || rhptee->isVoidType())) + else if (lhq.withoutObjCGCAttr().withoutObjCLifetime().compatiblyIncludes( + rhq.withoutObjCGCAttr().withoutObjCLifetime(), + S.getASTContext()) && + (lhptee->isVoidType() || rhptee->isVoidType())) ; // keep old // Treat lifetime mismatches as fatal. @@ -9137,7 +9137,7 @@ checkObjCPointerTypesForAssignment(Sema &S, QualType LHSType, QualType lhptee = LHSType->castAs()->getPointeeType(); QualType rhptee = RHSType->castAs()->getPointeeType(); - if (!lhptee.isAtLeastAsQualifiedAs(rhptee) && + if (!lhptee.isAtLeastAsQualifiedAs(rhptee, S.getASTContext()) && // make an exception for id

!LHSType->isObjCQualifiedIdType()) return Sema::CompatiblePointerDiscardsQualifiers; @@ -10834,7 +10834,8 @@ static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, // if both are pointers check if operation is valid wrt address spaces if (isLHSPointer && isRHSPointer) { - if (!LHSPointeeTy.isAddressSpaceOverlapping(RHSPointeeTy)) { + if (!LHSPointeeTy.isAddressSpaceOverlapping(RHSPointeeTy, + S.getASTContext())) { S.Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) << LHSExpr->getType() << RHSExpr->getType() << 1 /*arithmetic op*/ @@ -12365,7 +12366,8 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, if (LCanPointeeTy != RCanPointeeTy) { // Treat NULL constant as a special case in OpenCL. if (getLangOpts().OpenCL && !LHSIsNull && !RHSIsNull) { - if (!LCanPointeeTy.isAddressSpaceOverlapping(RCanPointeeTy)) { + if (!LCanPointeeTy.isAddressSpaceOverlapping(RCanPointeeTy, + getASTContext())) { Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) << LHSType << RHSType << 0 /* comparison */ diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 4e3e9681890f..616481d62de8 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4783,7 +4783,8 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, ToType->castAs()->getPointeeType().getAddressSpace(); LangAS AddrSpaceR = FromType->castAs()->getPointeeType().getAddressSpace(); - assert(Qualifiers::isAddressSpaceSupersetOf(AddrSpaceL, AddrSpaceR) && + assert(Qualifiers::isAddressSpaceSupersetOf(AddrSpaceL, AddrSpaceR, + getASTContext()) && "Invalid cast"); CastKind Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; @@ -6641,7 +6642,7 @@ static bool TryClassUnification(Sema &Self, Expr *From, Expr *To, // same type as, or a base class of, the class of T1, and // [cv2 > cv1]. if (FRec == TRec || FDerivedFromT) { - if (TTy.isAtLeastAsQualifiedAs(FTy)) { + if (TTy.isAtLeastAsQualifiedAs(FTy, Self.getASTContext())) { InitializedEntity Entity = InitializedEntity::InitializeTemporary(TTy); InitializationSequence InitSeq(Self, Entity, Kind, From); if (InitSeq) { @@ -7363,8 +7364,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, if (Q1.getAddressSpace() == Q2.getAddressSpace()) { Quals.setAddressSpace(Q1.getAddressSpace()); } else if (Steps.size() == 1) { - bool MaybeQ1 = Q1.isAddressSpaceSupersetOf(Q2); - bool MaybeQ2 = Q2.isAddressSpaceSupersetOf(Q1); + bool MaybeQ1 = Q1.isAddressSpaceSupersetOf(Q2, getASTContext()); + bool MaybeQ2 = Q2.isAddressSpaceSupersetOf(Q1, getASTContext()); if (MaybeQ1 == MaybeQ2) { // Exception for ptr size address spaces. Should be able to choose // either address space during comparison. diff --git a/clang/lib/Sema/SemaFixItUtils.cpp b/clang/lib/Sema/SemaFixItUtils.cpp index 2c85a5319430..1dad46fd6b94 100644 --- a/clang/lib/Sema/SemaFixItUtils.cpp +++ b/clang/lib/Sema/SemaFixItUtils.cpp @@ -24,7 +24,7 @@ bool ConversionFixItGenerator::compareTypesSimple(CanQualType From, Sema &S, SourceLocation Loc, ExprValueKind FromVK) { - if (!To.isAtLeastAsQualifiedAs(From)) + if (!To.isAtLeastAsQualifiedAs(From, S.getASTContext())) return false; From = From.getNonReferenceType(); @@ -41,8 +41,8 @@ bool ConversionFixItGenerator::compareTypesSimple(CanQualType From, const CanQualType FromUnq = From.getUnqualifiedType(); const CanQualType ToUnq = To.getUnqualifiedType(); - if ((FromUnq == ToUnq || (S.IsDerivedFrom(Loc, FromUnq, ToUnq)) ) && - To.isAtLeastAsQualifiedAs(From)) + if ((FromUnq == ToUnq || (S.IsDerivedFrom(Loc, FromUnq, ToUnq))) && + To.isAtLeastAsQualifiedAs(From, S.getASTContext())) return true; return false; } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index f13355bb93cb..00586dc60199 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -4718,7 +4718,7 @@ static void TryReferenceListInitialization(Sema &S, if (T1Quals.hasAddressSpace()) { Qualifiers T2Quals; (void)S.Context.getUnqualifiedArrayType(InitList->getType(), T2Quals); - if (!T1Quals.isAddressSpaceSupersetOf(T2Quals)) { + if (!T1Quals.isAddressSpaceSupersetOf(T2Quals, S.getASTContext())) { Sequence.SetFailed( InitializationSequence::FK_ReferenceInitDropsQualifiers); return; @@ -5324,8 +5324,9 @@ static void TryReferenceInitializationCore(Sema &S, // shall be an rvalue reference. // For address spaces, we interpret this to mean that an addr space // of a reference "cv1 T1" is a superset of addr space of "cv2 T2". - if (isLValueRef && !(T1Quals.hasConst() && !T1Quals.hasVolatile() && - T1Quals.isAddressSpaceSupersetOf(T2Quals))) { + if (isLValueRef && + !(T1Quals.hasConst() && !T1Quals.hasVolatile() && + T1Quals.isAddressSpaceSupersetOf(T2Quals, S.getASTContext()))) { if (S.Context.getCanonicalType(T2) == S.Context.OverloadTy) Sequence.SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); else if (ConvOvlResult && !Sequence.getFailedCandidateSet().empty()) @@ -5334,7 +5335,7 @@ static void TryReferenceInitializationCore(Sema &S, ConvOvlResult); else if (!InitCategory.isLValue()) Sequence.SetFailed( - T1Quals.isAddressSpaceSupersetOf(T2Quals) + T1Quals.isAddressSpaceSupersetOf(T2Quals, S.getASTContext()) ? InitializationSequence:: FK_NonConstLValueReferenceBindingToTemporary : InitializationSequence::FK_ReferenceInitDropsQualifiers); @@ -5519,7 +5520,7 @@ static void TryReferenceInitializationCore(Sema &S, unsigned T2CVRQuals = T2Quals.getCVRQualifiers(); if (RefRelationship == Sema::Ref_Related && ((T1CVRQuals | T2CVRQuals) != T1CVRQuals || - !T1Quals.isAddressSpaceSupersetOf(T2Quals))) { + !T1Quals.isAddressSpaceSupersetOf(T2Quals, S.getASTContext()))) { Sequence.SetFailed(InitializationSequence::FK_ReferenceInitDropsQualifiers); return; } @@ -5536,8 +5537,8 @@ static void TryReferenceInitializationCore(Sema &S, Sequence.AddReferenceBindingStep(cv1T1IgnoreAS, /*BindingTemporary=*/true); if (T1Quals.hasAddressSpace()) { - if (!Qualifiers::isAddressSpaceSupersetOf(T1Quals.getAddressSpace(), - LangAS::Default)) { + if (!Qualifiers::isAddressSpaceSupersetOf( + T1Quals.getAddressSpace(), LangAS::Default, S.getASTContext())) { Sequence.SetFailed( InitializationSequence::FK_ReferenceAddrspaceMismatchTemporary); return; @@ -8629,7 +8630,7 @@ static void emitBadConversionNotes(Sema &S, const InitializedEntity &entity, !fromDecl->isInvalidDecl() && !destDecl->isInvalidDecl() && !fromDecl->hasDefinition() && destPointeeType.getQualifiers().compatiblyIncludes( - fromPointeeType.getQualifiers())) + fromPointeeType.getQualifiers(), S.getASTContext())) S.Diag(fromDecl->getLocation(), diag::note_forward_class_conversion) << S.getASTContext().getTagDeclType(fromDecl) << S.getASTContext().getTagDeclType(destDecl); @@ -8907,7 +8908,7 @@ bool InitializationSequence::Diagnose(Sema &S, SourceType.getQualifiers() - NonRefType.getQualifiers(); if (!NonRefType.getQualifiers().isAddressSpaceSupersetOf( - SourceType.getQualifiers())) + SourceType.getQualifiers(), S.getASTContext())) S.Diag(Kind.getLocation(), diag::err_reference_bind_drops_quals) << NonRefType << SourceType << 1 /*addr space*/ << Args[0]->getSourceRange(); diff --git a/clang/lib/Sema/SemaObjC.cpp b/clang/lib/Sema/SemaObjC.cpp index 0359d18dd945..57eda18c2d8e 100644 --- a/clang/lib/Sema/SemaObjC.cpp +++ b/clang/lib/Sema/SemaObjC.cpp @@ -1350,7 +1350,7 @@ bool SemaObjC::isObjCWritebackConversion(QualType FromType, QualType ToType, // Make sure that we have compatible qualifiers. FromQuals.setObjCLifetime(Qualifiers::OCL_Autoreleasing); - if (!ToQuals.compatiblyIncludes(FromQuals)) + if (!ToQuals.compatiblyIncludes(FromQuals, getASTContext())) return false; // Remove qualifiers from the pointee type we're converting from; they diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 19d56759246d..47462b1a868d 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -18188,7 +18188,8 @@ buildDeclareReductionRef(Sema &SemaRef, SourceLocation Loc, SourceRange Range, Lookups, [&SemaRef, Ty, Loc](ValueDecl *D) -> ValueDecl * { if (!D->isInvalidDecl() && SemaRef.IsDerivedFrom(Loc, Ty, D->getType()) && - !Ty.isMoreQualifiedThan(D->getType())) + !Ty.isMoreQualifiedThan(D->getType(), + SemaRef.getASTContext())) return D; return nullptr; })) { @@ -21038,7 +21039,8 @@ static ExprResult buildUserDefinedMapperRef(Sema &SemaRef, Scope *S, Lookups, [&SemaRef, Type, Loc](ValueDecl *D) -> ValueDecl * { if (!D->isInvalidDecl() && SemaRef.IsDerivedFrom(Loc, Type, D->getType()) && - !Type.isMoreQualifiedThan(D->getType())) + !Type.isMoreQualifiedThan(D->getType(), + SemaRef.getASTContext())) return D; return nullptr; })) { @@ -21209,7 +21211,7 @@ static bool hasUserDefinedMapper(Sema &SemaRef, Scope *S, Lookups, [&SemaRef, Type, Loc](ValueDecl *D) -> ValueDecl * { if (!D->isInvalidDecl() && SemaRef.IsDerivedFrom(Loc, Type, D->getType()) && - !Type.isMoreQualifiedThan(D->getType())) + !Type.isMoreQualifiedThan(D->getType(), SemaRef.getASTContext())) return D; return nullptr; }); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 4aeceba128b2..1b4c76c840fe 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2963,7 +2963,7 @@ static QualType AdoptQualifiers(ASTContext &Context, QualType T, Qualifiers Qs){ if (TQs == Qs) return T; - if (Qs.compatiblyIncludes(TQs)) + if (Qs.compatiblyIncludes(TQs, Context)) return Context.getQualifiedType(T, Qs); return Context.getQualifiedType(T.getUnqualifiedType(), Qs); @@ -2997,7 +2997,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, const ObjCInterfaceType* RHS = FromObjCPtr->getInterfaceType(); if (getLangOpts().CPlusPlus && LHS && RHS && !ToObjCPtr->getPointeeType().isAtLeastAsQualifiedAs( - FromObjCPtr->getPointeeType())) + FromObjCPtr->getPointeeType(), getASTContext())) return false; ConvertedType = BuildSimilarlyQualifiedPointerType(FromObjCPtr, ToObjCPtr->getPointeeType(), @@ -3604,7 +3604,8 @@ static bool isNonTrivialObjCLifetimeConversion(Qualifiers FromQuals, static bool isQualificationConversionStep(QualType FromType, QualType ToType, bool CStyle, bool IsTopLevel, bool &PreviousToQualsIncludeConst, - bool &ObjCLifetimeConversion) { + bool &ObjCLifetimeConversion, + const ASTContext &Ctx) { Qualifiers FromQuals = FromType.getQualifiers(); Qualifiers ToQuals = ToType.getQualifiers(); @@ -3635,7 +3636,7 @@ static bool isQualificationConversionStep(QualType FromType, QualType ToType, // -- for every j > 0, if const is in cv 1,j then const is in cv // 2,j, and similarly for volatile. - if (!CStyle && !ToQuals.compatiblyIncludes(FromQuals)) + if (!CStyle && !ToQuals.compatiblyIncludes(FromQuals, Ctx)) return false; // If address spaces mismatch: @@ -3645,8 +3646,8 @@ static bool isQualificationConversionStep(QualType FromType, QualType ToType, // - in non-top levels it is not a valid conversion. if (ToQuals.getAddressSpace() != FromQuals.getAddressSpace() && (!IsTopLevel || - !(ToQuals.isAddressSpaceSupersetOf(FromQuals) || - (CStyle && FromQuals.isAddressSpaceSupersetOf(ToQuals))))) + !(ToQuals.isAddressSpaceSupersetOf(FromQuals, Ctx) || + (CStyle && FromQuals.isAddressSpaceSupersetOf(ToQuals, Ctx))))) return false; // -- if the cv 1,j and cv 2,j are different, then const is in @@ -3693,9 +3694,10 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType, bool PreviousToQualsIncludeConst = true; bool UnwrappedAnyPointer = false; while (Context.UnwrapSimilarTypes(FromType, ToType)) { - if (!isQualificationConversionStep( - FromType, ToType, CStyle, !UnwrappedAnyPointer, - PreviousToQualsIncludeConst, ObjCLifetimeConversion)) + if (!isQualificationConversionStep(FromType, ToType, CStyle, + !UnwrappedAnyPointer, + PreviousToQualsIncludeConst, + ObjCLifetimeConversion, getASTContext())) return false; UnwrappedAnyPointer = true; } @@ -4546,9 +4548,9 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc, T1 = S.Context.getQualifiedType(UnqualT1, T1Quals); if (isa(T2) && T2Quals) T2 = S.Context.getQualifiedType(UnqualT2, T2Quals); - if (T2.isMoreQualifiedThan(T1)) + if (T2.isMoreQualifiedThan(T1, S.getASTContext())) return ImplicitConversionSequence::Better; - if (T1.isMoreQualifiedThan(T2)) + if (T1.isMoreQualifiedThan(T2, S.getASTContext())) return ImplicitConversionSequence::Worse; } } @@ -4987,7 +4989,7 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, bool ObjCLifetimeConversion = false; if (!isQualificationConversionStep(T2, T1, /*CStyle=*/false, TopLevel, PreviousToQualsIncludeConst, - ObjCLifetimeConversion)) + ObjCLifetimeConversion, getASTContext())) return (ConvertedReferent || Context.hasSimilarType(T1, T2)) ? Ref_Related : Ref_Incompatible; @@ -5318,7 +5320,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType, // MS compiler ignores __unaligned qualifier for references; do the same. T1Quals.removeUnaligned(); T2Quals.removeUnaligned(); - if (!T1Quals.compatiblyIncludes(T2Quals)) + if (!T1Quals.compatiblyIncludes(T2Quals, S.getASTContext())) return ICS; } @@ -5836,7 +5838,7 @@ static ImplicitConversionSequence TryObjectArgumentInitialization( if (ImplicitParamType.getCVRQualifiers() != FromTypeCanon.getLocalCVRQualifiers() && !ImplicitParamType.isAtLeastAsQualifiedAs( - withoutUnaligned(S.Context, FromTypeCanon))) { + withoutUnaligned(S.Context, FromTypeCanon), S.getASTContext())) { ICS.setBad(BadConversionSequence::bad_qualifiers, FromType, ImplicitParamType); return ICS; @@ -5845,7 +5847,8 @@ static ImplicitConversionSequence TryObjectArgumentInitialization( if (FromTypeCanon.hasAddressSpace()) { Qualifiers QualsImplicitParamType = ImplicitParamType.getQualifiers(); Qualifiers QualsFromType = FromTypeCanon.getQualifiers(); - if (!QualsImplicitParamType.isAddressSpaceSupersetOf(QualsFromType)) { + if (!QualsImplicitParamType.isAddressSpaceSupersetOf(QualsFromType, + S.getASTContext())) { ICS.setBad(BadConversionSequence::bad_qualifiers, FromType, ImplicitParamType); return ICS; @@ -7030,7 +7033,7 @@ void Sema::AddOverloadCandidate( // destination address space. if (!Qualifiers::isAddressSpaceSupersetOf( Constructor->getMethodQualifiers().getAddressSpace(), - CandidateSet.getDestAS())) { + CandidateSet.getDestAS(), getASTContext())) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_object_addrspace_mismatch; } @@ -10678,9 +10681,9 @@ bool clang::isBetterOverloadCandidate( LangAS AS1 = CD1->getMethodQualifiers().getAddressSpace(); LangAS AS2 = CD2->getMethodQualifiers().getAddressSpace(); if (AS1 != AS2) { - if (Qualifiers::isAddressSpaceSupersetOf(AS2, AS1)) + if (Qualifiers::isAddressSpaceSupersetOf(AS2, AS1, S.getASTContext())) return true; - if (Qualifiers::isAddressSpaceSupersetOf(AS1, AS2)) + if (Qualifiers::isAddressSpaceSupersetOf(AS1, AS2, S.getASTContext())) return false; } } @@ -11285,7 +11288,7 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, } if (CToTy.getUnqualifiedType() == CFromTy.getUnqualifiedType() && - !CToTy.isAtLeastAsQualifiedAs(CFromTy)) { + !CToTy.isAtLeastAsQualifiedAs(CFromTy, S.getASTContext())) { Qualifiers FromQs = CFromTy.getQualifiers(); Qualifiers ToQs = CToTy.getQualifiers(); @@ -11384,7 +11387,7 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, if (const PointerType *FromPtrTy = FromTy->getAs()) { if (const PointerType *ToPtrTy = ToTy->getAs()) { if (ToPtrTy->getPointeeType().isAtLeastAsQualifiedAs( - FromPtrTy->getPointeeType()) && + FromPtrTy->getPointeeType(), S.getASTContext()) && !FromPtrTy->getPointeeType()->isIncompleteType() && !ToPtrTy->getPointeeType()->isIncompleteType() && S.IsDerivedFrom(SourceLocation(), ToPtrTy->getPointeeType(), @@ -11398,11 +11401,12 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, if (const ObjCInterfaceDecl *FromIface = FromPtrTy->getInterfaceDecl()) if (const ObjCInterfaceDecl *ToIface = ToPtrTy->getInterfaceDecl()) if (ToPtrTy->getPointeeType().isAtLeastAsQualifiedAs( - FromPtrTy->getPointeeType()) && + FromPtrTy->getPointeeType(), S.getASTContext()) && FromIface->isSuperClassOf(ToIface)) BaseToDerivedConversion = 2; } else if (const ReferenceType *ToRefTy = ToTy->getAs()) { - if (ToRefTy->getPointeeType().isAtLeastAsQualifiedAs(FromTy) && + if (ToRefTy->getPointeeType().isAtLeastAsQualifiedAs(FromTy, + S.getASTContext()) && !FromTy->isIncompleteType() && !ToRefTy->getPointeeType()->isIncompleteType() && S.IsDerivedFrom(SourceLocation(), ToRefTy->getPointeeType(), FromTy)) { diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 89fe4f42afb0..62a0c30d9950 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -1743,7 +1743,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( // C++ [temp.deduct.conv]p4: // If the original A is a reference type, A can be more cv-qualified // than the deduced A - if (!A.getQualifiers().compatiblyIncludes(P.getQualifiers())) + if (!A.getQualifiers().compatiblyIncludes(P.getQualifiers(), + S.getASTContext())) return TemplateDeductionResult::NonDeducedMismatch; // Strip out all extra qualifiers from the argument to figure out the @@ -3772,7 +3773,7 @@ CheckOriginalCallArgDeduction(Sema &S, TemplateDeductionInfo &Info, if (AQuals == DeducedAQuals) { // Qualifiers match; there's nothing to do. - } else if (!DeducedAQuals.compatiblyIncludes(AQuals)) { + } else if (!DeducedAQuals.compatiblyIncludes(AQuals, S.getASTContext())) { return Failed(); } else { // Qualifiers are compatible, so have the argument type adopt the diff --git a/clang/test/CodeGen/target-addrspace.cpp b/clang/test/CodeGen/target-addrspace.cpp new file mode 100644 index 000000000000..9adf53611bc2 --- /dev/null +++ b/clang/test/CodeGen/target-addrspace.cpp @@ -0,0 +1,140 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -triple nvptx64-nvidia-cuda -emit-llvm \ +// RUN: -fvisibility=hidden -o - %s | FileCheck %s --check-prefix=NVPTX +// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -emit-llvm \ +// RUN: -fvisibility=hidden -o - %s | FileCheck %s --check-prefix=AMDGPU + +// NVPTX-LABEL: define hidden void @_Z1fPv( +// NVPTX-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NVPTX-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: ret void +// +// AMDGPU-LABEL: define hidden void @_Z1fPv( +// AMDGPU-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8, addrspace(5) +// AMDGPU-NEXT: [[P_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[P_ADDR]] to ptr +// AMDGPU-NEXT: store ptr [[P]], ptr [[P_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: ret void +// +void f(void *p) {} + +// NVPTX-LABEL: define hidden void @_Z2p1Pv( +// NVPTX-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NVPTX-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: call void @_Z1fPv(ptr noundef [[TMP0]]) #[[ATTR1:[0-9]+]] +// NVPTX-NEXT: ret void +// +// AMDGPU-LABEL: define hidden void @_Z2p1Pv( +// AMDGPU-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8, addrspace(5) +// AMDGPU-NEXT: [[P_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[P_ADDR]] to ptr +// AMDGPU-NEXT: store ptr [[P]], ptr [[P_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: call void @_Z1fPv(ptr noundef [[TMP0]]) #[[ATTR1:[0-9]+]] +// AMDGPU-NEXT: ret void +// +void p1(void [[clang::address_space(0)]] * p) { f(p); } +// NVPTX-LABEL: define hidden noundef ptr @_Z2p2PU3AS3v( +// NVPTX-SAME: ptr addrspace(3) noundef [[P:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[P_ADDR:%.*]] = alloca ptr addrspace(3), align 8 +// NVPTX-NEXT: store ptr addrspace(3) [[P]], ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: [[TMP0:%.*]] = load ptr addrspace(3), ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: [[TMP1:%.*]] = addrspacecast ptr addrspace(3) [[TMP0]] to ptr +// NVPTX-NEXT: ret ptr [[TMP1]] +// +// AMDGPU-LABEL: define hidden noundef ptr @_Z2p2PU3AS3v( +// AMDGPU-SAME: ptr addrspace(3) noundef [[P:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca ptr, align 8, addrspace(5) +// AMDGPU-NEXT: [[P_ADDR:%.*]] = alloca ptr addrspace(3), align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[P_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[P_ADDR]] to ptr +// AMDGPU-NEXT: store ptr addrspace(3) [[P]], ptr [[P_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP0:%.*]] = load ptr addrspace(3), ptr [[P_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP1:%.*]] = addrspacecast ptr addrspace(3) [[TMP0]] to ptr +// AMDGPU-NEXT: ret ptr [[TMP1]] +// +void *p2(void [[clang::address_space(3)]] * p) { return p; } +// NVPTX-LABEL: define hidden noundef ptr @_Z2p3PU3AS3v( +// NVPTX-SAME: ptr addrspace(3) noundef [[P:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[P_ADDR:%.*]] = alloca ptr addrspace(3), align 8 +// NVPTX-NEXT: store ptr addrspace(3) [[P]], ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: [[TMP0:%.*]] = load ptr addrspace(3), ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: [[TMP1:%.*]] = addrspacecast ptr addrspace(3) [[TMP0]] to ptr +// NVPTX-NEXT: ret ptr [[TMP1]] +// +// AMDGPU-LABEL: define hidden noundef ptr @_Z2p3PU3AS3v( +// AMDGPU-SAME: ptr addrspace(3) noundef [[P:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[RETVAL:%.*]] = alloca ptr, align 8, addrspace(5) +// AMDGPU-NEXT: [[P_ADDR:%.*]] = alloca ptr addrspace(3), align 4, addrspace(5) +// AMDGPU-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr +// AMDGPU-NEXT: [[P_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[P_ADDR]] to ptr +// AMDGPU-NEXT: store ptr addrspace(3) [[P]], ptr [[P_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP0:%.*]] = load ptr addrspace(3), ptr [[P_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP1:%.*]] = addrspacecast ptr addrspace(3) [[TMP0]] to ptr +// AMDGPU-NEXT: ret ptr [[TMP1]] +// +void *p3(void [[clang::address_space(3)]] * p) { return p; } + +struct S { + S() = default; + ~S() = default; + void foo() {} +}; + +S s1; +S [[clang::address_space(1)]] s2; +S [[clang::address_space(3)]] s3; + +template void foo(Ty *) {} + +// NVPTX-LABEL: define hidden void @_Z2t1Pv( +// NVPTX-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NVPTX-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: call void @_Z3fooIvEvPT_(ptr noundef [[TMP0]]) #[[ATTR1]] +// NVPTX-NEXT: ret void +// +// AMDGPU-LABEL: define hidden void @_Z2t1Pv( +// AMDGPU-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8, addrspace(5) +// AMDGPU-NEXT: [[P_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[P_ADDR]] to ptr +// AMDGPU-NEXT: store ptr [[P]], ptr [[P_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR_ASCAST]], align 8 +// AMDGPU-NEXT: call void @_Z3fooIvEvPT_(ptr noundef [[TMP0]]) #[[ATTR1]] +// AMDGPU-NEXT: ret void +// +void t1(void *p) { foo(p); } +// NVPTX-LABEL: define hidden void @_Z2t3PU3AS3v( +// NVPTX-SAME: ptr addrspace(3) noundef [[P:%.*]]) #[[ATTR0]] { +// NVPTX-NEXT: [[ENTRY:.*:]] +// NVPTX-NEXT: [[P_ADDR:%.*]] = alloca ptr addrspace(3), align 8 +// NVPTX-NEXT: store ptr addrspace(3) [[P]], ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: [[TMP0:%.*]] = load ptr addrspace(3), ptr [[P_ADDR]], align 8 +// NVPTX-NEXT: call void @_Z3fooIU3AS3vEvPT_(ptr addrspace(3) noundef [[TMP0]]) #[[ATTR1]] +// NVPTX-NEXT: ret void +// +// AMDGPU-LABEL: define hidden void @_Z2t3PU3AS3v( +// AMDGPU-SAME: ptr addrspace(3) noundef [[P:%.*]]) #[[ATTR0]] { +// AMDGPU-NEXT: [[ENTRY:.*:]] +// AMDGPU-NEXT: [[P_ADDR:%.*]] = alloca ptr addrspace(3), align 4, addrspace(5) +// AMDGPU-NEXT: [[P_ADDR_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[P_ADDR]] to ptr +// AMDGPU-NEXT: store ptr addrspace(3) [[P]], ptr [[P_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: [[TMP0:%.*]] = load ptr addrspace(3), ptr [[P_ADDR_ASCAST]], align 4 +// AMDGPU-NEXT: call void @_Z3fooIU3AS3vEvPT_(ptr addrspace(3) noundef [[TMP0]]) #[[ATTR1]] +// AMDGPU-NEXT: ret void +// +void t3(void [[clang::address_space(3)]] *p) { foo(p); } diff --git a/clang/test/Sema/amdgcn-address-spaces.c b/clang/test/Sema/amdgcn-address-spaces.c new file mode 100644 index 000000000000..50c12993ac69 --- /dev/null +++ b/clang/test/Sema/amdgcn-address-spaces.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 %s -triple amdgcn-amd-amdhsa -fsyntax-only -verify + +#define _AS0 __attribute__((address_space(0))) +#define _AS1 __attribute__((address_space(1))) +#define _AS2 __attribute__((address_space(2))) +#define _AS3 __attribute__((address_space(3))) +#define _AS4 __attribute__((address_space(4))) +#define _AS5 __attribute__((address_space(5))) +#define _AS999 __attribute__((address_space(999))) + +void *p1(void _AS1 *p) { return p; } +void *p2(void _AS2 *p) { return p; } +void *p3(void _AS3 *p) { return p; } +void *p4(void _AS4 *p) { return p; } +void *p5(void _AS5 *p) { return p; } +void *pi(void _AS999 *p) { return p; } // expected-error {{returning '_AS999 void *' from a function with result type 'void *' changes address space of pointer}} +void *pc(void __attribute__((opencl_local)) *p) { return p; } // expected-error {{returning '__local void *' from a function with result type 'void *' changes address space of pointer}} +void _AS1 *r0(void _AS1 *p) { return p; } +void _AS1 *r1(void *p) { return p; } // expected-error {{returning 'void *' from a function with result type '_AS1 void *' changes address space of pointer}} +void _AS1 *r2(void *p) { return (void _AS1 *)p; } diff --git a/clang/test/Sema/nvptx-address-spaces.c b/clang/test/Sema/nvptx-address-spaces.c new file mode 100644 index 000000000000..184feef9612e --- /dev/null +++ b/clang/test/Sema/nvptx-address-spaces.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 %s -triple nvptx64-nvidia-cuda -fsyntax-only -verify + +#define _AS0 __attribute__((address_space(0))) +#define _AS1 __attribute__((address_space(1))) +#define _AS2 __attribute__((address_space(2))) +#define _AS3 __attribute__((address_space(3))) +#define _AS4 __attribute__((address_space(4))) +#define _AS5 __attribute__((address_space(5))) +#define _AS999 __attribute__((address_space(999))) + +void *p1(void _AS1 *p) { return p; } +void *p2(void _AS2 *p) { return p; } // expected-error {{returning '_AS2 void *' from a function with result type 'void *' changes address space of pointer}} +void *p3(void _AS3 *p) { return p; } +void *p4(void _AS4 *p) { return p; } +void *p5(void _AS5 *p) { return p; } +void *pi(void _AS999 *p) { return p; } // expected-error {{returning '_AS999 void *' from a function with result type 'void *' changes address space of pointer}} +void *pc(void __attribute__((opencl_local)) *p) { return p; } // expected-error {{returning '__local void *' from a function with result type 'void *' changes address space of pointer}} +void _AS1 *r0(void _AS1 *p) { return p; } +void _AS1 *r1(void *p) { return p; } // expected-error {{returning 'void *' from a function with result type '_AS1 void *' changes address space of pointer}} +void _AS1 *r2(void *p) { return (void _AS1 *)p; } +