Make the Preprocessor more memory efficient and improve macro instantiation diagnostics.

When a macro instantiation occurs, reserve a SLocEntry chunk with length the
full length of the macro definition source. Set the spelling location of this chunk
to point to the start of the macro definition and any tokens that are lexed directly
from the macro definition will get a location from this chunk with the appropriate offset.

For any tokens that come from argument expansion, '##' paste operator, etc. have their
instantiation location point at the appropriate place in the instantiated macro definition
(the argument identifier and the '##' token respectively).
This improves macro instantiation diagnostics:

Before:

t.c:5:9: error: invalid operands to binary expression ('struct S' and 'int')
int y = M(/);
        ^~~~
t.c:5:11: note: instantiated from:
int y = M(/);
          ^

After:

t.c:5:9: error: invalid operands to binary expression ('struct S' and 'int')
int y = M(/);
        ^~~~
t.c:3:20: note: instantiated from:
\#define M(op) (foo op 3);
                ~~~ ^  ~
t.c:5:11: note: instantiated from:
int y = M(/);
          ^

The memory savings for a candidate boost library that abuses the preprocessor are:

- 32% less SLocEntries (37M -> 25M)
- 30% reduction in PCH file size (900M -> 635M)
- 50% reduction in memory usage for the SLocEntry table (1.6G -> 800M)

llvm-svn: 134587
This commit is contained in:
Argyrios Kyrtzidis 2011-07-07 03:40:34 +00:00
parent c8f7e213b9
commit 41fb2d95a3
15 changed files with 325 additions and 82 deletions

View File

@ -36,6 +36,7 @@ class SourceManager;
class FileManager;
class FileEntry;
class LineTableInfo;
class LangOptions;
/// SrcMgr - Public enums and private classes that are part of the
/// SourceManager implementation.
@ -833,11 +834,46 @@ public:
/// \brief Returns true if the given MacroID location points at the first
/// token of the macro instantiation.
bool isAtStartOfMacroInstantiation(SourceLocation Loc) const;
bool isAtStartOfMacroInstantiation(SourceLocation Loc,
const LangOptions &LangOpts) const;
/// \brief Returns true if the given MacroID location points at the last
/// token of the macro instantiation.
bool isAtEndOfMacroInstantiation(SourceLocation Loc) const;
bool isAtEndOfMacroInstantiation(SourceLocation Loc,
const LangOptions &LangOpts) const;
/// \brief Given a specific chunk of a FileID (FileID with offset+length),
/// returns true if \arg Loc is inside that chunk and sets relative offset
/// (offset of \arg Loc from beginning of chunk) to \arg relativeOffset.
bool isInFileID(SourceLocation Loc,
FileID FID, unsigned offset, unsigned length,
unsigned *relativeOffset = 0) const {
assert(!FID.isInvalid());
if (Loc.isInvalid())
return false;
unsigned start = getSLocEntry(FID).getOffset() + offset;
unsigned end = start + length;
#ifndef NDEBUG
// Make sure offset/length describe a chunk inside the given FileID.
unsigned NextOffset;
if (FID.ID+1 == SLocEntryTable.size())
NextOffset = getNextOffset();
else
NextOffset = getSLocEntry(FID.ID+1).getOffset();
assert(start < NextOffset);
assert(end < NextOffset);
#endif
if (Loc.getOffset() >= start && Loc.getOffset() < end) {
if (relativeOffset)
*relativeOffset = Loc.getOffset() - start;
return true;
}
return false;
}
//===--------------------------------------------------------------------===//
// Line Table Manipulation Routines
@ -899,6 +935,19 @@ public:
/// \returns true if LHS source location comes before RHS, false otherwise.
bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const;
/// \brief Determines the order of 2 source locations in the "source location
/// address space".
static bool isBeforeInSourceLocationOffset(SourceLocation LHS,
SourceLocation RHS) {
return isBeforeInSourceLocationOffset(LHS, RHS.getOffset());
}
/// \brief Determines the order of a source location and a source location
/// offset in the "source location address space".
static bool isBeforeInSourceLocationOffset(SourceLocation LHS, unsigned RHS) {
return LHS.getOffset() < RHS;
}
// Iterators over FileInfos.
typedef llvm::DenseMap<const FileEntry*, SrcMgr::ContentCache*>
::const_iterator fileinfo_iterator;

View File

@ -43,6 +43,10 @@ class MacroInfo {
/// to.
llvm::SmallVector<Token, 8> ReplacementTokens;
/// \brief Length in characters of the macro definition.
mutable unsigned DefinitionLength;
mutable bool IsDefinitionLengthCached : 1;
/// IsFunctionLike - True if this macro is a function-like macro, false if it
/// is an object-like macro.
bool IsFunctionLike : 1;
@ -116,6 +120,13 @@ public:
/// getDefinitionEndLoc - Return the location of the last token in the macro.
///
SourceLocation getDefinitionEndLoc() const { return EndLocation; }
/// \brief Get length in characters of the macro definition.
unsigned getDefinitionLength(SourceManager &SM) const {
if (IsDefinitionLengthCached)
return DefinitionLength;
return getDefinitionLengthSlow(SM);
}
/// isIdenticalTo - Return true if the specified macro definition is equal to
/// this macro in spelling, arguments, and whitespace. This is used to emit
@ -232,6 +243,8 @@ public:
/// AddTokenToBody - Add the specified token to the replacement text for the
/// macro.
void AddTokenToBody(const Token &Tok) {
assert(!IsDefinitionLengthCached &&
"Changing replacement tokens after definition length got calculated");
ReplacementTokens.push_back(Tok);
}
@ -248,6 +261,9 @@ public:
assert(!IsDisabled && "Cannot disable an already-disabled macro!");
IsDisabled = true;
}
private:
unsigned getDefinitionLengthSlow(SourceManager &SM) const;
};
} // end namespace clang

View File

@ -63,6 +63,17 @@ class TokenLexer {
/// instantiated.
SourceLocation InstantiateLocStart, InstantiateLocEnd;
/// \brief Source location pointing at the source location entry chunk that
/// was reserved for the current macro instantiation.
SourceLocation MacroExpansionStart;
/// \brief The offset of the macro instantiation in the
/// "source location address space".
unsigned MacroStartSLocOffset;
/// \brief FileID/offset of the start of the macro definition.
std::pair<FileID, unsigned> MacroDefStartInfo;
/// Lexical information about the expansion point of the macro: the identifier
/// that the macro expanded from had these properties.
bool AtStartOfLine : 1;
@ -154,6 +165,11 @@ private:
/// source line of the instantiated buffer. Handle this by returning the
/// first token on the next line.
void HandleMicrosoftCommentPaste(Token &Tok);
/// \brief If \arg loc is a FileID and points inside the current macro
/// definition, returns the appropriate source location pointing at the
/// macro expansion source location entry.
SourceLocation getMacroExpansionLocation(SourceLocation loc) const;
};
} // end namespace clang

View File

@ -388,7 +388,7 @@ bool TransformActionsImpl::canInsert(SourceLocation loc) {
if (loc.isFileID())
return true;
return SM.isAtStartOfMacroInstantiation(loc);
return SM.isAtStartOfMacroInstantiation(loc, Ctx.getLangOptions());
}
bool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) {
@ -401,7 +401,7 @@ bool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) {
if (loc.isFileID())
return true;
return SM.isAtEndOfMacroInstantiation(loc);
return SM.isAtEndOfMacroInstantiation(loc, Ctx.getLangOptions());
}
bool TransformActionsImpl::canRemoveRange(SourceRange range) {

View File

@ -37,7 +37,7 @@ SourceLocation trans::findLocationAfterSemi(SourceLocation loc,
ASTContext &Ctx) {
SourceManager &SM = Ctx.getSourceManager();
if (loc.isMacroID()) {
if (!SM.isAtEndOfMacroInstantiation(loc))
if (!SM.isAtEndOfMacroInstantiation(loc, Ctx.getLangOptions()))
return SourceLocation();
loc = SM.getInstantiationRange(loc).second;
}

View File

@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
#include "clang/Lex/Lexer.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceManagerInternals.h"
#include "clang/Basic/Diagnostic.h"
@ -1216,73 +1217,56 @@ PresumedLoc SourceManager::getPresumedLoc(SourceLocation Loc) const {
/// \brief Returns true if the given MacroID location points at the first
/// token of the macro instantiation.
bool SourceManager::isAtStartOfMacroInstantiation(SourceLocation loc) const {
bool SourceManager::isAtStartOfMacroInstantiation(SourceLocation loc,
const LangOptions &LangOpts) const {
assert(loc.isValid() && loc.isMacroID() && "Expected a valid macro loc");
std::pair<FileID, unsigned> infoLoc = getDecomposedLoc(loc);
// FIXME: If the token comes from the macro token paste operator ('##')
// this function will always return false;
if (infoLoc.second > 0)
return false; // Does not point at the start of token.
unsigned FID = infoLoc.first.ID;
assert(FID > 1);
std::pair<SourceLocation, SourceLocation>
instRange = getImmediateInstantiationRange(loc);
SourceLocation instLoc =
getSLocEntry(infoLoc.first).getInstantiation().getInstantiationLocStart();
if (instLoc.isFileID())
return true; // No other macro instantiations, this is the first.
bool invalid = false;
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID-1, &invalid);
if (invalid)
return false;
// If the FileID immediately before it is a file then this is the first token
// in the macro.
if (Entry.isFile())
return true;
// If the FileID immediately before it (which is a macro token) is the
// immediate instantiated macro, check this macro token's location.
if (getFileID(instRange.second).ID == FID-1)
return isAtStartOfMacroInstantiation(instRange.first);
// If the FileID immediately before it (which is a macro token) came from a
// different instantiation, then this is the first token in the macro.
if (getInstantiationLoc(Entry.getInstantiation().getInstantiationLocStart())
!= getInstantiationLoc(loc))
return true;
// It is inside the macro or the last token in the macro.
return false;
return isAtStartOfMacroInstantiation(instLoc, LangOpts);
}
/// \brief Returns true if the given MacroID location points at the last
/// token of the macro instantiation.
bool SourceManager::isAtEndOfMacroInstantiation(SourceLocation loc) const {
bool SourceManager::isAtEndOfMacroInstantiation(SourceLocation loc,
const LangOptions &LangOpts) const {
assert(loc.isValid() && loc.isMacroID() && "Expected a valid macro loc");
unsigned FID = getFileID(loc).ID;
assert(FID > 1);
std::pair<SourceLocation, SourceLocation>
instRange = getInstantiationRange(loc);
// If there's no FileID after it, it is the last token in the macro.
if (FID+1 == sloc_entry_size())
return true;
bool invalid = false;
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID+1, &invalid);
if (invalid)
SourceLocation spellLoc = getSpellingLoc(loc);
unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, *this, LangOpts);
if (tokLen == 0)
return false;
// If the FileID immediately after it is a file or a macro token which
// came from a different instantiation, then this is the last token in the
// macro.
if (Entry.isFile())
return true;
if (getInstantiationLoc(Entry.getInstantiation().getInstantiationLocStart())
!= instRange.first)
return true;
std::pair<FileID, unsigned> infoLoc = getDecomposedLoc(loc);
unsigned FID = infoLoc.first.ID;
// It is inside the macro or the first token in the macro.
return false;
unsigned NextOffset;
if (FID+1 == sloc_entry_size())
NextOffset = getNextOffset();
else
NextOffset = getSLocEntry(FID+1).getOffset();
// FIXME: If the token comes from the macro token paste operator ('##')
// or the stringify operator ('#') this function will always return false;
assert(loc.getOffset() + tokLen < NextOffset);
if (loc.getOffset() + tokLen < NextOffset-1)
return false; // Does not point to the last token.
SourceLocation instLoc =
getSLocEntry(infoLoc.first).getInstantiation().getInstantiationLocEnd();
if (instLoc.isFileID())
return true; // No other macro instantiations.
return isAtEndOfMacroInstantiation(instLoc, LangOpts);
}
//===----------------------------------------------------------------------===//
@ -1479,8 +1463,6 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
// reflect the order that the tokens, pointed to by these locations, were
// instantiated (during parsing each token that is instantiated by a macro,
// expands the SLocEntries).
if (LHS.isMacroID() && RHS.isMacroID())
return LHS.getOffset() < RHS.getOffset();
std::pair<FileID, unsigned> LOffs = getDecomposedLoc(LHS);
std::pair<FileID, unsigned> ROffs = getDecomposedLoc(RHS);

View File

@ -683,7 +683,7 @@ SourceLocation Lexer::getLocForEndOfToken(SourceLocation Loc, unsigned Offset,
return SourceLocation();
if (Loc.isMacroID()) {
if (Offset > 0 || !SM.isAtEndOfMacroInstantiation(Loc))
if (Offset > 0 || !SM.isAtEndOfMacroInstantiation(Loc, Features))
return SourceLocation(); // Points inside the macro instantiation.
// Continue and find the location just after the macro instantiation.

View File

@ -185,7 +185,8 @@ MacroArgs::getPreExpArgument(unsigned Arg, const MacroInfo *MI,
/// a character literal for the Microsoft charize (#@) extension.
///
Token MacroArgs::StringifyArgument(const Token *ArgToks,
Preprocessor &PP, bool Charify) {
Preprocessor &PP, bool Charify,
SourceLocation hashInstLoc) {
Token Tok;
Tok.startToken();
Tok.setKind(Charify ? tok::char_constant : tok::string_literal);
@ -273,14 +274,15 @@ Token MacroArgs::StringifyArgument(const Token *ArgToks,
}
}
PP.CreateString(&Result[0], Result.size(), Tok);
PP.CreateString(&Result[0], Result.size(), Tok, hashInstLoc);
return Tok;
}
/// getStringifiedArgument - Compute, cache, and return the specified argument
/// that has been 'stringified' as required by the # operator.
const Token &MacroArgs::getStringifiedArgument(unsigned ArgNo,
Preprocessor &PP) {
Preprocessor &PP,
SourceLocation hashInstLoc) {
assert(ArgNo < NumUnexpArgTokens && "Invalid argument number!");
if (StringifiedArgs.empty()) {
StringifiedArgs.resize(getNumArguments());
@ -288,6 +290,7 @@ const Token &MacroArgs::getStringifiedArgument(unsigned ArgNo,
sizeof(StringifiedArgs[0])*getNumArguments());
}
if (StringifiedArgs[ArgNo].isNot(tok::string_literal))
StringifiedArgs[ArgNo] = StringifyArgument(getUnexpArgument(ArgNo), PP);
StringifiedArgs[ArgNo] = StringifyArgument(getUnexpArgument(ArgNo), PP,
/*Charify=*/false, hashInstLoc);
return StringifiedArgs[ArgNo];
}

View File

@ -20,6 +20,7 @@ namespace clang {
class MacroInfo;
class Preprocessor;
class Token;
class SourceLocation;
/// MacroArgs - An instance of this class captures information about
/// the formal arguments specified to a function-like macro invocation.
@ -86,7 +87,8 @@ public:
/// getStringifiedArgument - Compute, cache, and return the specified argument
/// that has been 'stringified' as required by the # operator.
const Token &getStringifiedArgument(unsigned ArgNo, Preprocessor &PP);
const Token &getStringifiedArgument(unsigned ArgNo, Preprocessor &PP,
SourceLocation hashInstLoc);
/// getNumArguments - Return the number of arguments passed into this macro
/// invocation.
@ -106,7 +108,8 @@ public:
/// a character literal for the Microsoft charize (#@) extension.
///
static Token StringifyArgument(const Token *ArgToks,
Preprocessor &PP, bool Charify = false);
Preprocessor &PP, bool Charify,
SourceLocation hashInstLoc);
/// deallocate - This should only be called by the Preprocessor when managing

View File

@ -25,6 +25,7 @@ MacroInfo::MacroInfo(SourceLocation DefLoc) : Location(DefLoc) {
IsUsed = false;
IsAllowRedefinitionsWithoutWarning = false;
IsWarnIfUnused = false;
IsDefinitionLengthCached = false;
ArgumentList = 0;
NumArguments = 0;
@ -43,11 +44,42 @@ MacroInfo::MacroInfo(const MacroInfo &MI, llvm::BumpPtrAllocator &PPAllocator) {
IsUsed = MI.IsUsed;
IsAllowRedefinitionsWithoutWarning = MI.IsAllowRedefinitionsWithoutWarning;
IsWarnIfUnused = MI.IsWarnIfUnused;
IsDefinitionLengthCached = MI.IsDefinitionLengthCached;
DefinitionLength = MI.DefinitionLength;
ArgumentList = 0;
NumArguments = 0;
setArgumentList(MI.ArgumentList, MI.NumArguments, PPAllocator);
}
unsigned MacroInfo::getDefinitionLengthSlow(SourceManager &SM) const {
assert(!IsDefinitionLengthCached);
IsDefinitionLengthCached = true;
if (ReplacementTokens.empty())
return (DefinitionLength = 0);
const Token &firstToken = ReplacementTokens.front();
const Token &lastToken = ReplacementTokens.back();
SourceLocation macroStart = firstToken.getLocation();
SourceLocation macroEnd = lastToken.getLocation();
assert(macroStart.isValid() && macroEnd.isValid());
assert((macroStart.isFileID() || firstToken.is(tok::comment)) &&
"Macro defined in macro?");
assert((macroEnd.isFileID() || lastToken.is(tok::comment)) &&
"Macro defined in macro?");
std::pair<FileID, unsigned>
startInfo = SM.getDecomposedInstantiationLoc(macroStart);
std::pair<FileID, unsigned>
endInfo = SM.getDecomposedInstantiationLoc(macroEnd);
assert(startInfo.first == endInfo.first &&
"Macro definition spanning multiple FileIDs ?");
assert(startInfo.second <= endInfo.second);
DefinitionLength = endInfo.second - startInfo.second;
DefinitionLength += lastToken.getLength();
return DefinitionLength;
}
/// isIdenticalTo - Return true if the specified macro definition is equal to
/// this macro in spelling, arguments, and whitespace. This is used to emit
/// duplicate definition warnings. This implements the rules in C99 6.10.3.

View File

@ -40,6 +40,28 @@ void TokenLexer::Init(Token &Tok, SourceLocation ILEnd, MacroArgs *Actuals) {
OwnsTokens = false;
DisableMacroExpansion = false;
NumTokens = Macro->tokens_end()-Macro->tokens_begin();
MacroExpansionStart = SourceLocation();
SourceManager &SM = PP.getSourceManager();
MacroStartSLocOffset = SM.getNextOffset();
if (NumTokens > 0) {
assert(Tokens[0].getLocation().isValid());
assert((Tokens[0].getLocation().isFileID() || Tokens[0].is(tok::comment)) &&
"Macro defined in macro?");
assert(InstantiateLocStart.isValid());
// Reserve a source location entry chunk for the length of the macro
// definition. Tokens that get lexed directly from the definition will
// have their locations pointing inside this chunk. This is to avoid
// creating separate source location entries for each token.
SourceLocation macroStart = SM.getInstantiationLoc(Tokens[0].getLocation());
MacroDefStartInfo = SM.getDecomposedLoc(macroStart);
MacroExpansionStart = SM.createInstantiationLoc(macroStart,
InstantiateLocStart,
InstantiateLocEnd,
Macro->getDefinitionLength(SM));
}
// If this is a function-like macro, expand the arguments and change
// Tokens to point to the expanded tokens.
@ -72,6 +94,7 @@ void TokenLexer::Init(const Token *TokArray, unsigned NumToks,
InstantiateLocStart = InstantiateLocEnd = SourceLocation();
AtStartOfLine = false;
HasLeadingSpace = false;
MacroExpansionStart = SourceLocation();
// Set HasLeadingSpace/AtStartOfLine so that the first token will be
// returned unmodified.
@ -119,13 +142,19 @@ void TokenLexer::ExpandFunctionArguments() {
int ArgNo = Macro->getArgumentNum(Tokens[i+1].getIdentifierInfo());
assert(ArgNo != -1 && "Token following # is not an argument?");
SourceLocation hashInstLoc;
if(InstantiateLocStart.isValid()) {
hashInstLoc = getMacroExpansionLocation(CurTok.getLocation());
assert(hashInstLoc.isValid() && "Expected '#' to come from definition");
}
Token Res;
if (CurTok.is(tok::hash)) // Stringify
Res = ActualArgs->getStringifiedArgument(ArgNo, PP);
Res = ActualArgs->getStringifiedArgument(ArgNo, PP, hashInstLoc);
else {
// 'charify': don't bother caching these.
Res = MacroArgs::StringifyArgument(ActualArgs->getUnexpArgument(ArgNo),
PP, true);
PP, true, hashInstLoc);
}
// The stringified/charified string leading space flag gets set to match
@ -185,6 +214,20 @@ void TokenLexer::ExpandFunctionArguments() {
unsigned NumToks = MacroArgs::getArgLength(ResultArgToks);
ResultToks.append(ResultArgToks, ResultArgToks+NumToks);
if(InstantiateLocStart.isValid()) {
SourceManager &SM = PP.getSourceManager();
SourceLocation curInst =
getMacroExpansionLocation(CurTok.getLocation());
assert(curInst.isValid() &&
"Expected arg identifier to come from definition");
for (unsigned i = FirstResult, e = ResultToks.size(); i != e; ++i) {
Token &Tok = ResultToks[i];
Tok.setLocation(SM.createInstantiationLoc(Tok.getLocation(),
curInst, curInst,
Tok.getLength()));
}
}
// If any tokens were substituted from the argument, the whitespace
// before the first token should match the whitespace of the arg
// identifier.
@ -220,6 +263,21 @@ void TokenLexer::ExpandFunctionArguments() {
ResultToks.append(ArgToks, ArgToks+NumToks);
if(InstantiateLocStart.isValid()) {
SourceManager &SM = PP.getSourceManager();
SourceLocation curInst =
getMacroExpansionLocation(CurTok.getLocation());
assert(curInst.isValid() &&
"Expected arg identifier to come from definition");
for (unsigned i = ResultToks.size() - NumToks, e = ResultToks.size();
i != e; ++i) {
Token &Tok = ResultToks[i];
Tok.setLocation(SM.createInstantiationLoc(Tok.getLocation(),
curInst, curInst,
Tok.getLength()));
}
}
// If this token (the macro argument) was supposed to get leading
// whitespace, transfer this information onto the first token of the
// expansion.
@ -333,17 +391,29 @@ void TokenLexer::Lex(Token &Tok) {
TokenIsFromPaste = true;
}
SourceManager &SM = PP.getSourceManager();
// The token's current location indicate where the token was lexed from. We
// need this information to compute the spelling of the token, but any
// diagnostics for the expanded token should appear as if they came from
// InstantiationLoc. Pull this information together into a new SourceLocation
// that captures all of this.
if (InstantiateLocStart.isValid()) { // Don't do this for token streams.
SourceManager &SM = PP.getSourceManager();
Tok.setLocation(SM.createInstantiationLoc(Tok.getLocation(),
InstantiateLocStart,
InstantiateLocEnd,
Tok.getLength()));
if (InstantiateLocStart.isValid() && // Don't do this for token streams.
// Check that the token's location was not already set properly.
SM.isBeforeInSourceLocationOffset(Tok.getLocation(),
MacroStartSLocOffset)) {
SourceLocation instLoc;
if (Tok.is(tok::comment)) {
instLoc = SM.createInstantiationLoc(Tok.getLocation(),
InstantiateLocStart,
InstantiateLocEnd,
Tok.getLength());
} else {
instLoc = getMacroExpansionLocation(Tok.getLocation());
assert(instLoc.isValid() &&
"Location for token not coming from definition was not set!");
}
Tok.setLocation(instLoc);
}
// If this is the first token, set the lexical properties of the token to
@ -381,9 +451,10 @@ void TokenLexer::Lex(Token &Tok) {
bool TokenLexer::PasteTokens(Token &Tok) {
llvm::SmallString<128> Buffer;
const char *ResultTokStrPtr = 0;
SourceLocation PasteOpLoc;
do {
// Consume the ## operator.
SourceLocation PasteOpLoc = Tokens[CurToken].getLocation();
PasteOpLoc = Tokens[CurToken].getLocation();
++CurToken;
assert(!isAtEnd() && "No token on the RHS of a paste operator!");
@ -509,12 +580,30 @@ bool TokenLexer::PasteTokens(Token &Tok) {
// Transfer properties of the LHS over the the Result.
Result.setFlagValue(Token::StartOfLine , Tok.isAtStartOfLine());
Result.setFlagValue(Token::LeadingSpace, Tok.hasLeadingSpace());
// Finally, replace LHS with the result, consume the RHS, and iterate.
++CurToken;
Tok = Result;
} while (!isAtEnd() && Tokens[CurToken].is(tok::hashhash));
// The token's current location indicate where the token was lexed from. We
// need this information to compute the spelling of the token, but any
// diagnostics for the expanded token should appear as if the token was
// instantiated from the (##) operator. Pull this information together into
// a new SourceLocation that captures all of this.
if (InstantiateLocStart.isValid()) {
SourceManager &SM = PP.getSourceManager();
SourceLocation pasteLocInst =
getMacroExpansionLocation(PasteOpLoc);
assert(pasteLocInst.isValid() &&
"Expected '##' to come from definition");
Tok.setLocation(SM.createInstantiationLoc(Tok.getLocation(),
pasteLocInst,
pasteLocInst,
Tok.getLength()));
}
// Now that we got the result token, it will be subject to expansion. Since
// token pasting re-lexes the result token in raw mode, identifier information
// isn't looked up. As such, if the result is an identifier, look up id info.
@ -558,3 +647,23 @@ void TokenLexer::HandleMicrosoftCommentPaste(Token &Tok) {
PP.HandleMicrosoftCommentPaste(Tok);
}
/// \brief If \arg loc is a FileID and points inside the current macro
/// definition, returns the appropriate source location pointing at the
/// macro expansion source location entry.
SourceLocation TokenLexer::getMacroExpansionLocation(SourceLocation loc) const {
assert(InstantiateLocStart.isValid() && MacroExpansionStart.isValid() &&
"Not appropriate for token streams");
assert(loc.isValid());
SourceManager &SM = PP.getSourceManager();
unsigned relativeOffset;
if (loc.isFileID() &&
SM.isInFileID(loc,
MacroDefStartInfo.first, MacroDefStartInfo.second,
Macro->getDefinitionLength(SM), &relativeOffset)) {
return MacroExpansionStart.getFileLocWithOffset(relativeOffset);
}
return SourceLocation();
}

View File

@ -310,7 +310,8 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
SourceLocation FILoc = Tok.getLocation();
const char *FIText = ": ";
const SourceManager &SM = PP.getSourceManager();
if (FILoc.isFileID() || SM.isAtStartOfMacroInstantiation(FILoc)) {
if (FILoc.isFileID() ||
SM.isAtStartOfMacroInstantiation(FILoc, getLang())) {
FILoc = SM.getInstantiationLoc(FILoc);
bool IsInvalid = false;
const char *SourcePtr =

View File

@ -1,18 +1,40 @@
// Test this without pch.
// RUN: %clang_cc1 -include %S/variables.h -fsyntax-only -verify %s
// RUN: %clang_cc1 -include %s -fsyntax-only -verify %s
// Test with pch.
// RUN: %clang_cc1 -emit-pch -o %t %S/variables.h
// RUN: %clang_cc1 -emit-pch -o %t %s
// RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s
#ifndef HEADER
#define HEADER
extern float y;
extern int *ip, x;
float z; // expected-note{{previous}}
int z2 = 17; // expected-note{{previous}}
#define MAKE_HAPPY(X) X##Happy
int MAKE_HAPPY(Very); // expected-note{{previous definition is here}}
#define A_MACRO_IN_THE_PCH 492
#define FUNCLIKE_MACRO(X, Y) X ## Y
#define PASTE2(x,y) x##y
#define PASTE1(x,y) PASTE2(x,y)
#define UNIQUE(x) PASTE1(x,__COUNTER__)
int UNIQUE(a); // a0
int UNIQUE(a); // a1
#else
int *ip2 = &x;
float *fp = &ip; // expected-warning{{incompatible pointer types}}
// FIXME:variables.h expected-note{{previous}}
double z; // expected-error{{redefinition}}
// FIXME:variables.h expected-note{{previous}}
int z2 = 18; // expected-error{{redefinition}}
double VeryHappy; // expected-error{{redefinition}}
// FIXME:variables.h expected-note{{previous definition is here}}
int Q = A_MACRO_IN_THE_PCH;
@ -21,3 +43,5 @@ int R = FUNCLIKE_MACRO(A_MACRO_, IN_THE_PCH);
int UNIQUE(a); // a2
int *Arr[] = { &a0, &a1, &a2 };
#endif

View File

@ -23,4 +23,3 @@ int MAKE_HAPPY(Very);
int UNIQUE(a); // a0
int UNIQUE(a); // a1

View File

@ -89,3 +89,12 @@ void test1(void) {
int y = x;
int z = y;
}
void test2(int x) {
#define VALUE2 VALUE+VALUE
#define VALUE3 VALUE+0
#define VALUE4(x) x+0
x = VALUE2 // expected-error{{expected ';' after expression}}
x = VALUE3 // expected-error{{expected ';' after expression}}
x = VALUE4(0) // expected-error{{expected ';' after expression}}
}