Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -1423,9 +1423,9 @@ QualType getInjectedClassNameType(CXXRecordDecl *Decl, QualType TST) const; - QualType getAttributedType(attr::Kind attrKind, - QualType modifiedType, - QualType equivalentType); + QualType getAttributedType(attr::Kind attrKind, QualType modifiedType, + QualType equivalentType, + IdentifierInfo *MacroII = nullptr); QualType getSubstTemplateTypeParmType(const TemplateTypeParmType *Replaced, QualType Replacement) const; Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -4315,13 +4315,21 @@ QualType ModifiedType; QualType EquivalentType; + // If the attribute this type holds is address_space and this attribute was + // declared as part of a macro, we store the macro identifier information. + // This will be used for printing the macro name instead of + // `__attribute__((address_space(n)))` in diagnostics relating to differing + // address_spaces between types. + IdentifierInfo *AddressSpaceMacroII; + AttributedType(QualType canon, attr::Kind attrKind, QualType modified, - QualType equivalent) + QualType equivalent, IdentifierInfo *addrSpaceMacroII) : Type(Attributed, canon, equivalent->isDependentType(), equivalent->isInstantiationDependentType(), equivalent->isVariablyModifiedType(), equivalent->containsUnexpandedParameterPack()), - ModifiedType(modified), EquivalentType(equivalent) { + ModifiedType(modified), EquivalentType(equivalent), + AddressSpaceMacroII(addrSpaceMacroII) { AttributedTypeBits.AttrKind = attrKind; } @@ -4332,6 +4340,8 @@ QualType getModifiedType() const { return ModifiedType; } QualType getEquivalentType() const { return EquivalentType; } + IdentifierInfo *getAddressSpaceMacroII() const { return AddressSpaceMacroII; } + bool hasAddressSpaceMacroII() const { return AddressSpaceMacroII != nullptr; } bool isSugared() const { return true; } QualType desugar() const { return getEquivalentType(); } @@ -4387,14 +4397,17 @@ static Optional stripOuterNullability(QualType &T); void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getAttrKind(), ModifiedType, EquivalentType); + Profile(ID, getAttrKind(), ModifiedType, EquivalentType, + AddressSpaceMacroII); } static void Profile(llvm::FoldingSetNodeID &ID, Kind attrKind, - QualType modified, QualType equivalent) { + QualType modified, QualType equivalent, + IdentifierInfo *addrSpaceMacroII) { ID.AddInteger(attrKind); ID.AddPointer(modified.getAsOpaquePtr()); ID.AddPointer(equivalent.getAsOpaquePtr()); + ID.AddPointer(addrSpaceMacroII); } static bool classof(const Type *T) { Index: include/clang/Lex/Preprocessor.h =================================================================== --- include/clang/Lex/Preprocessor.h +++ include/clang/Lex/Preprocessor.h @@ -325,6 +325,9 @@ /// This is used when loading a precompiled preamble. std::pair SkipMainFilePreamble; + typedef std::pair MacroPair; + llvm::SmallVector AttributeMacros; + public: struct PreambleSkipInfo { SourceLocation HashTokenLoc; @@ -341,6 +344,10 @@ ElseLoc(ElseLoc) {} }; + const llvm::SmallVectorImpl &getAttributeMacros() const { + return AttributeMacros; + } + private: friend class ASTReader; friend class MacroArgs; Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -3878,9 +3878,10 @@ QualType ASTContext::getAttributedType(attr::Kind attrKind, QualType modifiedType, - QualType equivalentType) { + QualType equivalentType, + IdentifierInfo *MacroII) { llvm::FoldingSetNodeID id; - AttributedType::Profile(id, attrKind, modifiedType, equivalentType); + AttributedType::Profile(id, attrKind, modifiedType, equivalentType, MacroII); void *insertPos = nullptr; AttributedType *type = AttributedTypes.FindNodeOrInsertPos(id, insertPos); @@ -3888,7 +3889,7 @@ QualType canon = getCanonicalType(equivalentType); type = new (*this, TypeAlignment) - AttributedType(canon, attrKind, modifiedType, equivalentType); + AttributedType(canon, attrKind, modifiedType, equivalentType, MacroII); Types.push_back(type); AttributedTypes.InsertNode(type, insertPos); Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ lib/AST/TypePrinter.cpp @@ -1364,7 +1364,12 @@ if (T->getAttrKind() == attr::ObjCKindOf) OS << "__kindof "; - printBefore(T->getModifiedType(), OS); + if (T->getAttrKind() == attr::AddressSpace && T->hasAddressSpaceMacroII()) { + OS << T->getAddressSpaceMacroII()->getName() << " "; + printBefore(T->getModifiedType().getUnqualifiedType(), OS); + } else { + printBefore(T->getModifiedType(), OS); + } if (T->isMSTypeSpec()) { switch (T->getAttrKind()) { Index: lib/Lex/PPDirectives.cpp =================================================================== --- lib/Lex/PPDirectives.cpp +++ lib/Lex/PPDirectives.cpp @@ -2577,6 +2577,31 @@ MI->setDefinitionEndLoc(LastTok.getLocation()); return MI; } + +// The minimum number of tokens used for an attribute. This includes the +// attribute keyword, 2 opening parentheses, 2 closing parenthesis, and at least +// one token for the attribute itself. +static constexpr unsigned kMinAttrTokens = 6; + +/// This only catches macros whose whole definition is an attribute. That is, it +/// starts with the attribute keyword and 2 opening parentheses, and ends with +/// the 2 closing parentheses. +static bool DefinesAttribute(const Preprocessor &PP, + const ArrayRef &Tokens, + SourceRange &Range) { + if (Tokens.size() < kMinAttrTokens) + return false; + if (Tokens[0].is(tok::kw___attribute) && Tokens[1].is(tok::l_paren) && + Tokens[2].is(tok::l_paren) && + Tokens[Tokens.size() - 2].is(tok::r_paren) && + Tokens[Tokens.size() - 1].is(tok::r_paren)) { + Range.setBegin(Tokens.front().getLocation()); + Range.setEnd(Tokens.back().getLocation()); + return true; + } + return false; +} + /// HandleDefineDirective - Implements \#define. This consumes the entire macro /// line then lets the caller lex the next real token. void Preprocessor::HandleDefineDirective( @@ -2692,6 +2717,13 @@ WarnUnusedMacroLocs.insert(MI->getDefinitionLoc()); } + // If the macro is an attribute that contains address_space(), save this for + // diagnosing later. + SourceRange Range; + if (DefinesAttribute(*this, MD->getInfo()->tokens(), Range)) + AttributeMacros.push_back( + std::make_pair(Range, MacroNameTok.getIdentifierInfo())); + // If the callbacks want to know, tell them about the macro definition. if (Callbacks) Callbacks->MacroDefined(MacroNameTok, MD); Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -240,12 +240,60 @@ diagnoseBadTypeAttribute(getSema(), *Attr, type); } + static bool SourceLocInSourceRange(SourceLocation SpellingLoc, + SourceRange Range, + const SourceManager &SM) { + SourceLocation RangeStart = Range.getBegin(); + SourceLocation RangeEnd = Range.getEnd(); + unsigned LineNo = SM.getSpellingLineNumber(SpellingLoc); + unsigned StartLineNo = SM.getSpellingLineNumber(RangeStart); + unsigned EndLineNo = SM.getSpellingLineNumber(RangeEnd); + unsigned ColNo = SM.getSpellingColumnNumber(SpellingLoc); + unsigned StartColNo = SM.getSpellingColumnNumber(RangeStart); + unsigned EndColNo = SM.getSpellingColumnNumber(RangeEnd); + + // Outside of lines + if (LineNo < StartLineNo || LineNo > EndLineNo) + return false; + + // On the same line + if (LineNo == StartLineNo && ColNo < StartColNo) + return false; + if (LineNo == EndLineNo && ColNo > EndColNo) + return false; + + return true; + } + /// Get an attributed type for the given attribute, and remember the Attr /// object so that we can attach it to the AttributedTypeLoc. QualType getAttributedType(Attr *A, QualType ModifiedType, QualType EquivType) { - QualType T = - sema.Context.getAttributedType(A->getKind(), ModifiedType, EquivType); + IdentifierInfo *MacroII = nullptr; + SourceLocation start = A->getRange().getBegin(); + if (start.isMacroID()) { + // Walk the macro expansion + SourceLocation Loc = start; + auto &SM = sema.SourceMgr; + while (Loc.isMacroID() && !MacroII) { + for (const auto &MacroPair : sema.PP.getAttributeMacros()) { + SourceRange Range = MacroPair.first; + if (SourceLocInSourceRange(Loc, Range, sema.SourceMgr)) { + // Found the attribute declared in a macro. + MacroII = MacroPair.second; + break; + } + } + + FileID FID = SM.getFileID(Loc); + const SrcMgr::SLocEntry *E = &SM.getSLocEntry(FID); + const SrcMgr::ExpansionInfo &Expansion = E->getExpansion(); + Loc = Expansion.getExpansionLocStart(); + } + } + + QualType T = sema.Context.getAttributedType(A->getKind(), ModifiedType, + EquivType, MacroII); AttrsForTypes.push_back({cast(T.getTypePtr()), A}); AttrsForTypesSorted = false; return T; Index: test/Sema/address_space_print_macro.c =================================================================== --- /dev/null +++ test/Sema/address_space_print_macro.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 %s -fsyntax-only -verify + +#define AS1 __attribute__((address_space(1))) +#define AS2 __attribute__((address_space(2), annotate("foo"))) + +#define AS(i) address_space(i) +#define AS3 __attribute__((AS(3))) + +char *cmp(AS1 char *x, AS2 char *y) { + return x < y ? x : y; // expected-error{{conditional operator with the second and third operands of type ('AS1 char *' and 'AS2 char *') which are pointers to non-overlapping address spaces}} +} + +__attribute__((address_space(1))) char test_array[10]; +void test3(void) { + extern void test3_helper(char *p); // expected-note{{passing argument to parameter 'p' here}} + test3_helper(test_array); // expected-error{{passing '__attribute__((address_space(1))) char *' to parameter of type 'char *' changes address space of pointer}} +} + +char AS2 *test4_array; +void test4(void) { + extern void test3_helper(char *p); // expected-note{{passing argument to parameter 'p' here}} + test3_helper(test4_array); // expected-error{{passing 'AS2 char *' to parameter of type 'char *' changes address space of pointer}} +} + +void func() { + char AS1 *x; + char AS3 *x2; + char *y; + y = x; // expected-error{{assigning 'AS1 char *' to 'char *' changes address space of pointer}} + y = x2; // expected-error{{assigning 'AS3 char *' to 'char *' changes address space of pointer}} +} Index: test/Sema/address_spaces.c =================================================================== --- test/Sema/address_spaces.c +++ test/Sema/address_spaces.c @@ -71,5 +71,5 @@ // Clang extension doesn't forbid operations on pointers to different address spaces. char* cmp(_AS1 char *x, _AS2 char *y) { - return x < y ? x : y; // expected-error{{conditional operator with the second and third operands of type ('__attribute__((address_space(1))) char *' and '__attribute__((address_space(2))) char *') which are pointers to non-overlapping address spaces}} + return x < y ? x : y; // expected-error{{conditional operator with the second and third operands of type ('_AS1 char *' and '_AS2 char *') which are pointers to non-overlapping address spaces}} }