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: include/clang/Sema/ParsedAttr.h =================================================================== --- include/clang/Sema/ParsedAttr.h +++ include/clang/Sema/ParsedAttr.h @@ -168,6 +168,7 @@ private: IdentifierInfo *AttrName; IdentifierInfo *ScopeName; + IdentifierInfo *MacroII = nullptr; SourceRange AttrRange; SourceLocation ScopeLoc; SourceLocation EllipsisLoc; @@ -534,6 +535,12 @@ return getPropertyDataBuffer().SetterId; } + void setMacroII(IdentifierInfo *MacroName) { MacroII = MacroName; } + + bool hasMacroII() const { return MacroII != nullptr; } + + IdentifierInfo *getMacroII() const { return MacroII; } + /// Get an index into the attribute spelling list /// defined in Attr.td. This index is used by an attribute /// to pretty print itself. 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,18 @@ if (T->getAttrKind() == attr::ObjCKindOf) OS << "__kindof "; - printBefore(T->getModifiedType(), OS); + if (T->getAttrKind() == attr::AddressSpace && T->hasAddressSpaceMacroII()) { + OS << T->getAddressSpaceMacroII()->getName() << " "; + + // Remove the underlying address_space so it won't be printed. + SplitQualType SplitTy = T->getModifiedType().split(); + Qualifiers Quals = SplitTy.Quals; + if (Quals.getAddressSpace() >= LangAS::FirstTargetAddressSpace) + Quals.removeAddressSpace(); + printBefore(SplitTy.Ty, Quals, 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/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -87,6 +87,39 @@ #undef CLANG_ATTR_LATE_PARSED_LIST } +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; +} + +/// For the last N given parsed attributes, apply the identifier info of a macro +/// they were defined in. +static void ApplyMacroIIToParsedAttrs(ParsedAttributes &attrs, unsigned N, + IdentifierInfo *MacroII) { + assert(attrs.size() >= N); + for (unsigned i = attrs.size() - N; i < N; ++i) + attrs[i].setMacroII(MacroII); +} + /// ParseGNUAttributes - Parse a non-empty attributes list. /// /// [GNU] attributes: @@ -133,6 +166,8 @@ LateParsedAttrList *LateAttrs, Declarator *D) { assert(Tok.is(tok::kw___attribute) && "Not a GNU attribute list!"); + unsigned OldNumAttrs = attrs.size(); + Token AttrTok = Tok; while (Tok.is(tok::kw___attribute)) { ConsumeToken(); @@ -203,6 +238,18 @@ SkipUntil(tok::r_paren, StopAtSemi); if (endLoc) *endLoc = Loc; + + unsigned NumParsedAttrs = attrs.size() - OldNumAttrs; + + // If this was declared in a macro, attatch the macro IdentifierInfo to the + // parsed attribute. + for (const auto &MacroPair : PP.getAttributeMacros()) { + if (SourceLocInSourceRange(AttrTok.getLocation(), MacroPair.first, + PP.getSourceManager())) { + ApplyMacroIIToParsedAttrs(attrs, NumParsedAttrs, MacroPair.second); + break; + } + } } } Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -243,9 +243,10 @@ /// 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); + QualType EquivType, + IdentifierInfo *MacroII = nullptr) { + QualType T = sema.Context.getAttributedType(A->getKind(), ModifiedType, + EquivType, MacroII); AttrsForTypes.push_back({cast(T.getTypePtr()), A}); AttrsForTypesSorted = false; return T; @@ -5817,7 +5818,7 @@ auto *ASAttr = ::new (Ctx) AddressSpaceAttr( Attr.getRange(), Ctx, Attr.getAttributeSpellingListIndex(), static_cast(T.getQualifiers().getAddressSpace())); - Type = State.getAttributedType(ASAttr, T, T); + Type = State.getAttributedType(ASAttr, T, T, Attr.getMacroII()); } else { Attr.setInvalid(); } 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}} }