9cdbf1d0eb
clingUtils modernize nullptr
2127 lines
83 KiB
C++
2127 lines
83 KiB
C++
//------------------------------------------------------------------------------
|
|
// CLING - the C++ LLVM-based InterpreterG :)
|
|
// author: Vassil Vassilev <vvasilev@cern.ch>
|
|
//
|
|
// This file is dual-licensed: you can choose to license it under the University
|
|
// of Illinois Open Source License or the GNU Lesser General Public License. See
|
|
// LICENSE.TXT for details.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "cling/Interpreter/LookupHelper.h"
|
|
#include "cling/Utils/Output.h"
|
|
|
|
#include "DeclUnloader.h"
|
|
#include "cling/Interpreter/Interpreter.h"
|
|
#include "cling/Utils/AST.h"
|
|
#include "cling/Utils/ParserStateRAII.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Parse/Parser.h"
|
|
#include "clang/Parse/RAIIObjectsForParser.h"
|
|
#include "clang/Sema/Scope.h"
|
|
#include "clang/Sema/Lookup.h"
|
|
#include "clang/Sema/Overload.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "clang/Sema/Template.h"
|
|
#include "clang/Sema/TemplateDeduction.h"
|
|
|
|
using namespace clang;
|
|
|
|
namespace cling {
|
|
///\brief Class to help with the custom allocation of clang::Expr
|
|
///
|
|
struct ExprAlloc {
|
|
char fBuffer[sizeof(clang::OpaqueValueExpr)];
|
|
};
|
|
|
|
class StartParsingRAII {
|
|
LookupHelper& m_LH;
|
|
llvm::SaveAndRestore<bool> SaveIsRecursivelyRunning;
|
|
// Save and restore the state of the Parser and lexer.
|
|
// Note: ROOT::Internal::ParsingStateRAII also save and restore the state of
|
|
// Sema, including pending instantiation for example. It is not clear
|
|
// whether we need to do so here too or whether we need to also see the
|
|
// "on-going" semantic information ... For now, we leave Sema untouched.
|
|
clang::Preprocessor::CleanupAndRestoreCacheRAII fCleanupRAII;
|
|
clang::Parser::ParserCurTokRestoreRAII fSavedCurToken;
|
|
ParserStateRAII ResetParserState;
|
|
clang::Sema::SFINAETrap fSFINAETrap;
|
|
void prepareForParsing(llvm::StringRef code, llvm::StringRef bufferName,
|
|
LookupHelper::DiagSetting diagOnOff);
|
|
public:
|
|
StartParsingRAII(LookupHelper& LH, llvm::StringRef code,
|
|
llvm::StringRef bufferName,
|
|
LookupHelper::DiagSetting diagOnOff)
|
|
: m_LH(LH), SaveIsRecursivelyRunning(LH.IsRecursivelyRunning),
|
|
fCleanupRAII(LH.m_Parser->getPreprocessor()),
|
|
fSavedCurToken(*LH.m_Parser),
|
|
ResetParserState(*LH.m_Parser,
|
|
!LH.IsRecursivelyRunning /*skipToEOF*/),
|
|
fSFINAETrap(m_LH.m_Parser->getActions()) {
|
|
LH.IsRecursivelyRunning = true;
|
|
prepareForParsing(code, bufferName, diagOnOff);
|
|
}
|
|
|
|
~StartParsingRAII() { pop(); }
|
|
void pop() const {}
|
|
};
|
|
|
|
void StartParsingRAII::prepareForParsing(llvm::StringRef code,
|
|
llvm::StringRef bufferName,
|
|
LookupHelper::DiagSetting diagOnOff) {
|
|
++m_LH.m_TotalParseRequests;
|
|
Parser& P = *m_LH.m_Parser;
|
|
Sema& S = P.getActions();
|
|
Preprocessor& PP = P.getPreprocessor();
|
|
//
|
|
// Tell the diagnostic engine to ignore all diagnostics.
|
|
//
|
|
P.getActions().getDiagnostics().setSuppressAllDiagnostics(
|
|
diagOnOff == LookupHelper::NoDiagnostics);
|
|
//
|
|
// Tell Sema we are not in the process of doing an instantiation.
|
|
// fSFINAETrap will reset any SFINAE error count of a SFINAE context from "above".
|
|
// fSFINAETrap will reset this value to the previous one; the line below is overwriting
|
|
// the value set by fSFINAETrap.
|
|
P.getActions().InNonInstantiationSFINAEContext = true;
|
|
//
|
|
// Tell the parser to not attempt spelling correction.
|
|
//
|
|
const_cast<LangOptions&>(PP.getLangOpts()).SpellChecking = 0;
|
|
//
|
|
// Turn on ignoring of the main file eof token.
|
|
//
|
|
// Note: We need this because token readahead in the following
|
|
// routine calls ends up parsing it multiple times.
|
|
//
|
|
if (!PP.isIncrementalProcessingEnabled()) {
|
|
PP.enableIncrementalProcessing();
|
|
}
|
|
assert(!code.empty() &&
|
|
"prepareForParsing should only be called when need");
|
|
|
|
// Create a fake file to parse the type name.
|
|
FileID FID;
|
|
llvm::hash_code hashedCode = llvm::hash_value(code);
|
|
auto cacheItr = m_LH.m_ParseBufferCache.find(hashedCode);
|
|
SourceLocation NewLoc;
|
|
SourceManager& SM = S.getSourceManager();
|
|
bool CacheIsValid = false;
|
|
if (cacheItr != m_LH.m_ParseBufferCache.end()) {
|
|
SourceLocation FileStartLoc =
|
|
SourceLocation::getFromRawEncoding(cacheItr->second);
|
|
FID = SM.getFileID(FileStartLoc);
|
|
|
|
bool Invalid = true;
|
|
llvm::StringRef FIDContents = SM.getBufferData(FID, &Invalid);
|
|
|
|
// A FileID is a (cached via ContentCache) SourceManager view of a
|
|
// FileManager::FileEntry (which is a wrapper on the file system file).
|
|
// In a subtle cases, code unloading can remove the cached region.
|
|
// However we are safe because it will empty the ContentCache and force
|
|
// the FileEntry to be re-read. It will keep the FileID intact and valid
|
|
// by design. When we reprocess the same (but modified) file it will get
|
|
// a new FileID. Then the Invalid flag will be false but the underlying
|
|
// buffer content will be empty. It will not compare equal to the lookup
|
|
// string and we will avoid using (a potentially broken) cache.
|
|
assert(!Invalid);
|
|
|
|
// Compare the contents of the cached buffer and the string we should
|
|
// process. If there are hash collisions this assert should trigger
|
|
// making it easier to debug.
|
|
CacheIsValid = FIDContents.equals(llvm::StringRef(code.str() + "\n"));
|
|
assert(CacheIsValid && "Hash collision!");
|
|
if (CacheIsValid) {
|
|
// We have already included this file once. Reuse the include loc.
|
|
NewLoc = SM.getIncludeLoc(FID);
|
|
// The Preprocessor will try to set the NumCreatedFIDs but we are
|
|
// reparsing and this value was already set. Force reset it to avoid
|
|
// triggering an assertion in the setNumCreatedFIDsForFileID routine.
|
|
SM.setNumCreatedFIDsForFileID(FID, 0, /*force*/ true);
|
|
++m_LH.m_CacheHits;
|
|
}
|
|
}
|
|
if (!CacheIsValid) {
|
|
std::unique_ptr<llvm::MemoryBuffer> SB
|
|
= llvm::MemoryBuffer::getMemBufferCopy(code.str() + "\n",
|
|
bufferName.str());
|
|
NewLoc = m_LH.m_Interpreter->getNextAvailableLoc();
|
|
FID = SM.createFileID(std::move(SB), SrcMgr::C_User, /*LoadedID*/0,
|
|
/*LoadedOffset*/0, NewLoc);
|
|
SourceLocation FileStartLoc = SM.getLocForStartOfFile(FID);
|
|
m_LH.m_ParseBufferCache[hashedCode] = FileStartLoc.getRawEncoding();
|
|
}
|
|
|
|
//
|
|
// Switch to the new file the way #include does.
|
|
//
|
|
// Note: To switch back to the main file we must consume an eof token.
|
|
//
|
|
PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc);
|
|
PP.Lex(const_cast<Token&>(P.getCurToken()));
|
|
}
|
|
|
|
// pin *tor here so that we can have clang::Parser defined and be able to call
|
|
// the dtor on the OwningPtr
|
|
LookupHelper::LookupHelper(clang::Parser* P, Interpreter* interp)
|
|
: m_Parser(P), m_Interpreter(interp) {
|
|
}
|
|
|
|
LookupHelper::~LookupHelper() {}
|
|
|
|
static
|
|
DeclContext* getCompleteContext(const Decl* scopeDecl,
|
|
ASTContext& Context, Sema &S);
|
|
|
|
static const TagDecl* RequireCompleteDeclContext(Sema& S,
|
|
Preprocessor& PP,
|
|
const TagDecl *tobeCompleted,
|
|
LookupHelper::DiagSetting diagOnOff)
|
|
{
|
|
// getContextAndSpec create the CXXScopeSpec and requires the scope
|
|
// to be complete, so this is exactly what we need.
|
|
|
|
bool OldSuppressAllDiagnostics(PP.getDiagnostics()
|
|
.getSuppressAllDiagnostics());
|
|
PP.getDiagnostics().setSuppressAllDiagnostics(
|
|
diagOnOff == LookupHelper::NoDiagnostics);
|
|
|
|
ASTContext& Context = S.getASTContext();
|
|
DeclContext* complete = getCompleteContext(tobeCompleted,Context,S);
|
|
|
|
PP.getDiagnostics().setSuppressAllDiagnostics(OldSuppressAllDiagnostics);
|
|
|
|
if (!complete)
|
|
return 0;
|
|
if (const TagDecl *result = dyn_cast<TagDecl>(complete))
|
|
return result->getDefinition();
|
|
return 0;
|
|
}
|
|
|
|
///\brief Look for a tag decl based on its name
|
|
///
|
|
///\param declName name of the class, enum, uniorn or namespace being
|
|
/// looked for
|
|
///\param resultDecl pointer that will be updated with the answer
|
|
///\param P Parse to use for the search
|
|
///\param diagOnOff whether the error diagnostics are printed or not.
|
|
///\return returns true if the answer is authoritative or false if a more
|
|
/// detailed search is needed (usually this is for class template
|
|
/// instances).
|
|
///
|
|
static bool quickFindDecl(llvm::StringRef declName,
|
|
const Decl *& resultDecl,
|
|
Parser &P,
|
|
LookupHelper::DiagSetting diagOnOff) {
|
|
|
|
Sema &S = P.getActions();
|
|
Preprocessor &PP = P.getPreprocessor();
|
|
resultDecl = nullptr;
|
|
const clang::DeclContext *sofar = nullptr;
|
|
const clang::Decl *next = nullptr;
|
|
for (size_t c = 0, last = 0; c < declName.size(); ++c) {
|
|
const char current = declName[c];
|
|
if (current == '<' || current == '>' ||
|
|
current == ' ' || current == '&' ||
|
|
current == '*' || current == '[' ||
|
|
current == ']') {
|
|
// For now we do not know how to deal with
|
|
// template instances.
|
|
return false;
|
|
}
|
|
if (current == ':') {
|
|
if (c + 2 >= declName.size() || declName[c + 1] != ':') {
|
|
// Looks like an invalid name, we won't find anything.
|
|
return true;
|
|
}
|
|
next = utils::Lookup::Named(&S, declName.substr(last, c - last), sofar);
|
|
if (next == (void *) -1) {
|
|
// Ambiguous result, we need to go through the long path
|
|
return false;
|
|
} else if (next && next != (void *) -1) {
|
|
// Need to handle typedef here too.
|
|
const TypedefNameDecl *typedefDecl = dyn_cast<TypedefNameDecl>(next);
|
|
if (typedefDecl) {
|
|
// We are stripping the typedef, this is technically incorrect,
|
|
// as the result (if resultType has been specified) will not be
|
|
// an accurate representation of the input string.
|
|
// As we strip the typedef we ought to rebuild the nested name
|
|
// specifier.
|
|
// Since we do not use this path for template handling, this
|
|
// is not relevant for ROOT itself ....
|
|
ASTContext &Context = S.getASTContext();
|
|
QualType T = Context.getTypedefType(typedefDecl);
|
|
const TagType *TagTy = T->getAs<TagType>();
|
|
if (TagTy) next = TagTy->getDecl();
|
|
}
|
|
|
|
// To use Lookup::Named we need to fit the assertion:
|
|
// ((!isa<TagDecl>(LookupCtx) || LookupCtx->isDependentContext()
|
|
// || cast<TagDecl>(LookupCtx)->isCompleteDefinition()
|
|
// || cast<TagDecl>(LookupCtx)->isBeingDefined()) &&
|
|
// "Declaration context must already be complete!"),
|
|
// function LookupQualifiedName, file SemaLookup.cpp, line 1614.
|
|
const clang::TagDecl *tdecl = dyn_cast<TagDecl>(next);
|
|
if (tdecl && !(next = tdecl->getDefinition())) {
|
|
//fprintf(stderr,"Incomplete (inner) type for %s (part %s).\n",
|
|
// declName.str().c_str(),
|
|
// declName.substr(last,c-last).str().c_str());
|
|
// Incomplete type we will not be able to go on.
|
|
|
|
// We always require completeness of the scope, if the caller
|
|
// want piece-meal instantiation, the calling code will need to
|
|
// split the call to findScope.
|
|
|
|
// if (instantiateTemplate) {
|
|
if (dyn_cast<ClassTemplateSpecializationDecl>(tdecl)) {
|
|
// Go back to the normal schedule since we need a valid point
|
|
// of instantiation:
|
|
// Assertion failed: (Loc.isValid() &&
|
|
// "point of instantiation must be valid!"),
|
|
// function setPointOfInstantiation, file DeclTemplate.h,
|
|
// line 1520.
|
|
// Which can happen here because the simple name maybe a
|
|
// typedef to a template (for example std::string).
|
|
return false;
|
|
}
|
|
next = RequireCompleteDeclContext(S, PP, tdecl, diagOnOff);
|
|
// } else {
|
|
// return false;
|
|
// }
|
|
}
|
|
sofar = dyn_cast_or_null<DeclContext>(next);
|
|
} else {
|
|
sofar = 0;
|
|
}
|
|
if (!sofar) {
|
|
// We are looking into something that is not a decl context,
|
|
// so we won't find anything.
|
|
return true;
|
|
}
|
|
last = c + 2;
|
|
++c; // Consume the second ':'
|
|
} else if (c + 1 == declName.size()) {
|
|
// End of the line.
|
|
next = utils::Lookup::Named(&S, declName.substr(last, c + 1 - last), sofar);
|
|
// If there is an ambiguity, we need to go the long route.
|
|
if (next == (void *) -1) return false;
|
|
if (next) {
|
|
resultDecl = next;
|
|
}
|
|
return true;
|
|
}
|
|
} // for each characters
|
|
// Should be unreacheable.
|
|
return false;
|
|
}
|
|
|
|
static QualType findBuiltinType(llvm::StringRef typeName, ASTContext &Context)
|
|
{
|
|
bool issigned = false;
|
|
bool isunsigned = false;
|
|
if (typeName.startswith("signed ")) {
|
|
issigned = true;
|
|
typeName = StringRef(typeName.data()+7, typeName.size()-7);
|
|
}
|
|
if (!issigned && typeName.startswith("unsigned ")) {
|
|
isunsigned = true;
|
|
typeName = StringRef(typeName.data()+9, typeName.size()-9);
|
|
}
|
|
if (typeName.equals("char")) {
|
|
if (isunsigned) return Context.UnsignedCharTy;
|
|
return Context.SignedCharTy;
|
|
}
|
|
if (typeName.equals("short")) {
|
|
if (isunsigned) return Context.UnsignedShortTy;
|
|
return Context.ShortTy;
|
|
}
|
|
if (typeName.equals("int")) {
|
|
if (isunsigned) return Context.UnsignedIntTy;
|
|
return Context.IntTy;
|
|
}
|
|
if (typeName.equals("long")) {
|
|
if (isunsigned) return Context.UnsignedLongTy;
|
|
return Context.LongTy;
|
|
}
|
|
if (typeName.equals("long long")) {
|
|
if (isunsigned) return Context.UnsignedLongLongTy;
|
|
return Context.LongLongTy;
|
|
}
|
|
if (!issigned && !isunsigned) {
|
|
if (typeName.equals("bool")) return Context.BoolTy;
|
|
if (typeName.equals("float")) return Context.FloatTy;
|
|
if (typeName.equals("double")) return Context.DoubleTy;
|
|
if (typeName.equals("long double")) return Context.LongDoubleTy;
|
|
|
|
if (typeName.equals("wchar_t")) return Context.WCharTy;
|
|
if (typeName.equals("char16_t")) return Context.Char16Ty;
|
|
if (typeName.equals("char32_t")) return Context.Char32Ty;
|
|
}
|
|
/* Missing
|
|
CanQualType WideCharTy; // Same as WCharTy in C++, integer type in C99.
|
|
CanQualType WIntTy; // [C99 7.24.1], integer type unchanged by default promotions.
|
|
*/
|
|
return QualType();
|
|
}
|
|
|
|
///\brief Look for a tag decl based on its name
|
|
///
|
|
///\param typeName name of the class, enum, uniorn or namespace being
|
|
/// looked for
|
|
///\param resultType reference to QualType that will be updated with the answer
|
|
///\param P Parse to use for the search
|
|
///\param diagOnOff whether the error diagnostics are printed or not.
|
|
///\return returns true if the answer is authoritative or false if a more
|
|
/// detailed search is needed (usually this is for class template
|
|
/// instances).
|
|
///
|
|
static bool quickFindType(llvm::StringRef typeName,
|
|
QualType &resultType,
|
|
Parser &P,
|
|
LookupHelper::DiagSetting diagOnOff) {
|
|
|
|
resultType = QualType();
|
|
|
|
llvm::StringRef quickTypeName = typeName.trim();
|
|
bool innerConst = false;
|
|
bool outerConst = false;
|
|
if (quickTypeName.startswith("const ")) {
|
|
// Use this syntax to avoid the redudant tests in substr.
|
|
quickTypeName = StringRef(quickTypeName.data()+6,
|
|
quickTypeName.size()-6);
|
|
innerConst = true;
|
|
}
|
|
|
|
enum PointerType { kPointerType, kLRefType, kRRefType, };
|
|
|
|
if (quickTypeName.endswith("const")) {
|
|
if (quickTypeName.size() < 6) return true;
|
|
auto c = quickTypeName[quickTypeName.size()-6];
|
|
if (c==' ' || c=='&' || c=='*') {
|
|
outerConst = true;
|
|
if (c == ' ')
|
|
quickTypeName = StringRef(quickTypeName.data(),
|
|
quickTypeName.size() - 6);
|
|
else quickTypeName = StringRef(quickTypeName.data(),
|
|
quickTypeName.size() - 5);
|
|
}
|
|
}
|
|
std::vector<PointerType> ptrref;
|
|
for(auto c = quickTypeName.end()-1; c != quickTypeName.begin(); --c) {
|
|
if (*c == '*') ptrref.push_back(kPointerType);
|
|
else if (*c == '&') {
|
|
if (*(c-1)== '&') {
|
|
--c;
|
|
ptrref.push_back(kRRefType);
|
|
|
|
} else
|
|
ptrref.push_back(kLRefType);
|
|
}
|
|
else break;
|
|
}
|
|
if (!ptrref.empty()) quickTypeName = StringRef(quickTypeName.data(),quickTypeName.size()-ptrref.size());
|
|
|
|
Sema &S = P.getActions();
|
|
ASTContext &Context = S.getASTContext();
|
|
QualType quickFind = findBuiltinType(quickTypeName, Context);
|
|
const Decl *quickDecl = nullptr;
|
|
if (quickFind.isNull() &&
|
|
quickFindDecl(quickTypeName, quickDecl, P, diagOnOff)) {
|
|
// The result of quickFindDecl was definitive, we don't need
|
|
// to check any further.
|
|
//const TypeDecl *typedecl = dyn_cast<TypeDecl>(quickDecl);
|
|
if (quickDecl) {
|
|
const TypeDecl *typedecl = dyn_cast<TypeDecl>(quickDecl);
|
|
if (typedecl) {
|
|
quickFind = Context.getTypeDeclType(typedecl);
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
if (!quickFind.isNull()) {
|
|
if (innerConst && !quickFind->isReferenceType()) quickFind.addConst();
|
|
|
|
for(auto t : ptrref) {
|
|
switch (t) {
|
|
case kPointerType :
|
|
quickFind = Context.getPointerType(quickFind);
|
|
break;
|
|
case kLRefType :
|
|
quickFind = Context.getLValueReferenceType(quickFind);
|
|
break;
|
|
case kRRefType :
|
|
quickFind = Context.getRValueReferenceType(quickFind);
|
|
break;
|
|
}
|
|
}
|
|
if (outerConst && !quickFind->isReferenceType()) quickFind.addConst();
|
|
resultType = quickFind;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QualType LookupHelper::findType(llvm::StringRef typeName,
|
|
DiagSetting diagOnOff) const {
|
|
//
|
|
// Our return value.
|
|
//
|
|
QualType TheQT;
|
|
|
|
if (typeName.empty()) return TheQT;
|
|
|
|
// Could trigger deserialization of decls.
|
|
Interpreter::PushTransactionRAII RAII(m_Interpreter);
|
|
|
|
// Deal with the most common case.
|
|
// Going through this custom finder is both much faster
|
|
// (6 times faster, 10.6s to 57.5s for 1 000 000 calls) and consumes
|
|
// infinite less memory (0B vs 181 B per call for 'Float_t*').
|
|
QualType quickFind;
|
|
if (quickFindType(typeName,quickFind, *m_Parser, diagOnOff)) {
|
|
// The result of quickFindDecl was definitive, we don't need
|
|
// to check any further.
|
|
return quickFind;
|
|
}
|
|
|
|
// Use P for shortness
|
|
Parser& P = *m_Parser;
|
|
StartParsingRAII ParseStarted(const_cast<LookupHelper&>(*this),
|
|
typeName,
|
|
llvm::StringRef("lookup.type.by.name.file"),
|
|
diagOnOff);
|
|
//
|
|
// Try parsing the type name.
|
|
//
|
|
clang::ParsedAttributes Attrs(P.getAttrFactory());
|
|
// FIXME: All arguments to ParseTypeName are the default arguments. Remove.
|
|
TypeResult Res(P.ParseTypeName(0, DeclaratorContext::TypeName,
|
|
clang::AS_none, 0, &Attrs));
|
|
if (Res.isUsable()) {
|
|
// Accept it only if the whole name was parsed.
|
|
if (P.NextToken().getKind() == clang::tok::eof) {
|
|
TypeSourceInfo* TSI = 0;
|
|
TheQT = clang::Sema::GetTypeFromParser(Res.get(), &TSI);
|
|
}
|
|
}
|
|
// if (!quickFind.isNull() && !TheQT.isNull() && TheQT != quickFind) {
|
|
// fprintf(stderr,"Different result\n");
|
|
// fprintf(stderr,"quickFindType:"); quickFind.dump();
|
|
// fprintf(stderr,"TheQT :"); TheQT.dump();
|
|
//
|
|
// }
|
|
return TheQT;
|
|
}
|
|
|
|
const Decl* LookupHelper::findScope(llvm::StringRef className,
|
|
DiagSetting diagOnOff,
|
|
const Type** resultType /* = nullptr */,
|
|
bool instantiateTemplate/*=true*/) const {
|
|
|
|
//
|
|
// Some utilities.
|
|
//
|
|
// Use P for shortness
|
|
Parser &P = *m_Parser;
|
|
Sema &S = P.getActions();
|
|
Preprocessor &PP = P.getPreprocessor();
|
|
ASTContext &Context = S.getASTContext();
|
|
|
|
|
|
// The user wants to see the template instantiation, existing or not.
|
|
// Here we might not have an active transaction to handle
|
|
// the caused instantiation decl.
|
|
// Also quickFindDecl could trigger deserialization of decls.
|
|
Interpreter::PushTransactionRAII pushedT(m_Interpreter);
|
|
|
|
// See if we can find it without a buffer and any clang parsing,
|
|
// We need to go scope by scope.
|
|
{
|
|
const Decl *quickResult = nullptr;
|
|
if (quickFindDecl(className, quickResult, *m_Parser, diagOnOff)) {
|
|
// The result of quickFindDecl was definitive, we don't need
|
|
// to check any further.
|
|
if (!quickResult) {
|
|
return nullptr;
|
|
} else {
|
|
const TagDecl *tagdecl = dyn_cast<TagDecl>(quickResult);
|
|
const TypedefNameDecl *typedefDecl = dyn_cast<TypedefNameDecl>(quickResult);
|
|
if (typedefDecl) {
|
|
QualType T = Context.getTypedefType(typedefDecl);
|
|
const TagType *TagTy = T->getAs<TagType>();
|
|
if (TagTy) tagdecl = TagTy->getDecl();
|
|
// NOTE: Should we instantiate here? ... maybe ...
|
|
if (tagdecl && resultType) *resultType = T.getTypePtr();
|
|
|
|
} else if (tagdecl && resultType) {
|
|
*resultType = tagdecl->getTypeForDecl();
|
|
}
|
|
// fprintf(stderr,"Short cut taken for %s.\n",className.str().c_str());
|
|
if (tagdecl) {
|
|
const TagDecl *defdecl = tagdecl->getDefinition();
|
|
if (!defdecl || !defdecl->isCompleteDefinition()) {
|
|
// fprintf(stderr,"Incomplete type for %s.\n",className.str().c_str());
|
|
if (instantiateTemplate) {
|
|
if (dyn_cast<ClassTemplateSpecializationDecl>(tagdecl)) {
|
|
// Go back to the normal schedule since we need a valid point
|
|
// of instantiation:
|
|
// Assertion failed: (Loc.isValid() &&
|
|
// "point of instantiation must be valid!"),
|
|
// function setPointOfInstantiation, file DeclTemplate.h,
|
|
// line 1520.
|
|
// Which can happen here because the simple name maybe a
|
|
// typedef to a template (for example std::string).
|
|
|
|
// break;
|
|
// the next code executed must be the TransactionRAII below
|
|
} else
|
|
return RequireCompleteDeclContext(S, PP, tagdecl, diagOnOff);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
} else {
|
|
return defdecl; // now pointing to the definition.
|
|
}
|
|
} else if (isa<NamespaceDecl>(quickResult)) {
|
|
return quickResult->getCanonicalDecl();
|
|
} else if (auto alias = dyn_cast<NamespaceAliasDecl>(quickResult)) {
|
|
return alias->getNamespace()->getCanonicalDecl();
|
|
} else {
|
|
//fprintf(stderr,"Not a scope decl for %s.\n",className.str().c_str());
|
|
// The name exist and does not point to a 'scope' decl.
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
StartParsingRAII ParseStarted(const_cast<LookupHelper&>(*this),
|
|
className.str() + "::",
|
|
llvm::StringRef("lookup.class.by.name.file"),
|
|
diagOnOff);
|
|
//
|
|
// Our return values.
|
|
//
|
|
const Type* TheType = 0;
|
|
const Type** setResultType = &TheType;
|
|
if (resultType)
|
|
setResultType = resultType;
|
|
*setResultType = 0;
|
|
|
|
//
|
|
// Prevent failing on an assert in TryAnnotateCXXScopeToken.
|
|
//
|
|
if (!P.getCurToken().is(clang::tok::identifier)
|
|
&& !P.getCurToken().is(clang::tok::coloncolon)
|
|
&& !(P.getCurToken().is(clang::tok::annot_template_id)
|
|
&& P.NextToken().is(clang::tok::coloncolon))
|
|
&& !P.getCurToken().is(clang::tok::kw_decltype)) {
|
|
// error path
|
|
return 0;
|
|
}
|
|
//
|
|
// Try parsing the name as a nested-name-specifier.
|
|
//
|
|
if (P.TryAnnotateCXXScopeToken(false)) {
|
|
// error path
|
|
return 0;
|
|
}
|
|
|
|
Decl* TheDecl = 0;
|
|
|
|
if (P.getCurToken().getKind() == tok::annot_cxxscope) {
|
|
CXXScopeSpec SS;
|
|
S.RestoreNestedNameSpecifierAnnotation(P.getCurToken().getAnnotationValue(),
|
|
P.getCurToken().getAnnotationRange(),
|
|
SS);
|
|
if (SS.isValid()) {
|
|
NestedNameSpecifier* NNS = SS.getScopeRep();
|
|
NestedNameSpecifier::SpecifierKind Kind = NNS->getKind();
|
|
// Only accept the parse if we consumed all of the name.
|
|
if (P.NextToken().getKind() == clang::tok::eof) {
|
|
//
|
|
// Be careful, not all nested name specifiers refer to classes
|
|
// and namespaces, and those are the only things we want.
|
|
//
|
|
switch (Kind) {
|
|
case NestedNameSpecifier::Identifier: {
|
|
// Dependent type.
|
|
// We do not accept these.
|
|
}
|
|
break;
|
|
case NestedNameSpecifier::Namespace: {
|
|
// Namespace.
|
|
NamespaceDecl* NSD = NNS->getAsNamespace();
|
|
NSD = NSD->getCanonicalDecl();
|
|
TheDecl = NSD;
|
|
}
|
|
break;
|
|
case NestedNameSpecifier::NamespaceAlias: {
|
|
// Namespace alias.
|
|
// Note: In the future, should we return the alias instead?
|
|
NamespaceAliasDecl* NSAD = NNS->getAsNamespaceAlias();
|
|
NamespaceDecl* NSD = NSAD->getNamespace();
|
|
NSD = NSD->getCanonicalDecl();
|
|
TheDecl = NSD;
|
|
}
|
|
break;
|
|
case NestedNameSpecifier::TypeSpec:
|
|
// Type name.
|
|
// Intentional fall-though
|
|
case NestedNameSpecifier::TypeSpecWithTemplate: {
|
|
// Type name qualified with "template".
|
|
// Note: Do we need to check for a dependent type here?
|
|
NestedNameSpecifier *prefix = NNS->getPrefix();
|
|
if (prefix) {
|
|
QualType temp
|
|
= Context.getElaboratedType(ETK_None,prefix,
|
|
QualType(NNS->getAsType(),0));
|
|
*setResultType = temp.getTypePtr();
|
|
} else {
|
|
*setResultType = NNS->getAsType();
|
|
}
|
|
const TagType* TagTy = (*setResultType)->getAs<TagType>();
|
|
if (TagTy) {
|
|
// It is a class, struct, or union.
|
|
TagDecl* TD = TagTy->getDecl();
|
|
if (TD) {
|
|
TheDecl = TD->getDefinition();
|
|
// NOTE: if (TheDecl) ... check for theDecl->isInvalidDecl()
|
|
if (TD && TD->isInvalidDecl()) {
|
|
printf("Warning: FindScope got an invalid tag decl\n");
|
|
}
|
|
if (TheDecl && TheDecl->isInvalidDecl()) {
|
|
printf("ERROR: FindScope about to return an invalid decl\n");
|
|
}
|
|
if (!TheDecl && instantiateTemplate) {
|
|
|
|
// Make sure it is not just forward declared, and
|
|
// instantiate any templates.
|
|
DeclContext *ctxt = TD;
|
|
if (!S.RequireCompleteDeclContext(SS, ctxt)) {
|
|
// Success, type is complete, instantiations have
|
|
// been done.
|
|
TheDecl = TD->getDefinition();
|
|
if (TheDecl->isInvalidDecl()) {
|
|
// if the decl is invalid try to clean up
|
|
UnloadDecl(&S, TheDecl);
|
|
*setResultType = nullptr;
|
|
return 0;
|
|
}
|
|
} else {
|
|
// NOTE: We cannot instantiate the scope: not a valid decl.
|
|
// Need to unload it if this decl is a definition.
|
|
// But do not unload pre-existing fwd decls. Note that this might have failed
|
|
// because several other Decls failed to instantiate, leaving several Decls
|
|
// in invalid state. We should be unloading all of them, i.e. inload the
|
|
// current (possibly nested) transaction.
|
|
auto *T = const_cast<Transaction*>(m_Interpreter->getCurrentTransaction());
|
|
// Must not unload the Transaction, which might delete
|
|
// it: the RAII above still points to it! Instead, just
|
|
// mark it as "erroneous" which causes the RAII to
|
|
// unload it in due time.
|
|
T->setIssuedDiags(Transaction::kErrors);
|
|
*setResultType = nullptr;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case clang::NestedNameSpecifier::Global: {
|
|
// Name was just "::" and nothing more.
|
|
TheDecl = Context.getTranslationUnitDecl();
|
|
}
|
|
break;
|
|
case NestedNameSpecifier::Super:
|
|
// Microsoft's __super::
|
|
return 0;
|
|
}
|
|
return TheDecl;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Cleanup after failed parse as a nested-name-specifier.
|
|
//
|
|
P.SkipUntil(clang::tok::eof);
|
|
// Doesn't reset the diagnostic mappings
|
|
S.getDiagnostics().Reset(/*soft=*/true);
|
|
//
|
|
// Setup to reparse as a type.
|
|
//
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer>
|
|
SB(llvm::MemoryBuffer::getMemBufferCopy(className.str() + "\n",
|
|
"lookup.type.file"));
|
|
SourceLocation NewLoc = m_Interpreter->getNextAvailableLoc();
|
|
FileID FID = S.getSourceManager().createFileID(std::move(SB),
|
|
SrcMgr::C_User,
|
|
/*LoadedID*/0,
|
|
/*LoadedOffset*/0, NewLoc);
|
|
PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc);
|
|
PP.Lex(const_cast<clang::Token&>(P.getCurToken()));
|
|
|
|
//
|
|
// Now try to parse the name as a type.
|
|
//
|
|
if (P.TryAnnotateTypeOrScopeToken()) {
|
|
// error path
|
|
return 0;
|
|
}
|
|
if (P.getCurToken().getKind() == tok::annot_typename) {
|
|
TypeResult T = P.getTypeAnnotation(const_cast<Token&>(P.getCurToken()));
|
|
// Only accept the parse if we consumed all of the name.
|
|
if (P.NextToken().getKind() == clang::tok::eof)
|
|
if (!T.get().get().isNull()) {
|
|
TypeSourceInfo *TSI = 0;
|
|
clang::QualType QT =
|
|
clang::Sema::GetTypeFromParser(T.get(), &TSI);
|
|
if (const TagType* TT = QT->getAs<TagType>()) {
|
|
TheDecl = TT->getDecl()->getDefinition();
|
|
*setResultType = QT.getTypePtr();
|
|
}
|
|
}
|
|
}
|
|
return TheDecl;
|
|
}
|
|
|
|
const ClassTemplateDecl* LookupHelper::findClassTemplate(llvm::StringRef Name,
|
|
DiagSetting diagOnOff) const {
|
|
//
|
|
// Find a class template decl given its name.
|
|
//
|
|
|
|
if (Name.empty()) return 0;
|
|
|
|
// Humm ... this seems to do the trick ... or does it? or is there a better way?
|
|
|
|
// Use P for shortness
|
|
Parser& P = *m_Parser;
|
|
Sema& S = P.getActions();
|
|
ASTContext& Context = S.getASTContext();
|
|
StartParsingRAII ParseStarted(const_cast<LookupHelper&>(*this),
|
|
Name.str(),
|
|
llvm::StringRef("lookup.class.by.name.file"),
|
|
diagOnOff);
|
|
|
|
//
|
|
// Prevent failing on an assert in TryAnnotateCXXScopeToken.
|
|
//
|
|
if (!P.getCurToken().is(clang::tok::identifier)
|
|
&& !P.getCurToken().is(clang::tok::coloncolon)
|
|
&& !(P.getCurToken().is(clang::tok::annot_template_id)
|
|
&& P.NextToken().is(clang::tok::coloncolon))
|
|
&& !P.getCurToken().is(clang::tok::kw_decltype)) {
|
|
// error path
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Now try to parse the name as a type.
|
|
//
|
|
if (P.TryAnnotateTypeOrScopeToken()) {
|
|
// error path
|
|
return 0;
|
|
}
|
|
DeclContext *where = 0;
|
|
if (P.getCurToken().getKind() == tok::annot_cxxscope) {
|
|
CXXScopeSpec SS;
|
|
S.RestoreNestedNameSpecifierAnnotation(P.getCurToken().getAnnotationValue(),
|
|
P.getCurToken().getAnnotationRange(),
|
|
SS);
|
|
if (SS.isValid()) {
|
|
P.ConsumeAnyToken();
|
|
if (!P.getCurToken().is(clang::tok::identifier)) {
|
|
return 0;
|
|
}
|
|
NestedNameSpecifier *nested = SS.getScopeRep();
|
|
if (!nested) return 0;
|
|
switch (nested->getKind()) {
|
|
case NestedNameSpecifier::Global:
|
|
where = Context.getTranslationUnitDecl();
|
|
break;
|
|
case NestedNameSpecifier::Namespace:
|
|
where = nested->getAsNamespace();
|
|
break;
|
|
case NestedNameSpecifier::NamespaceAlias:
|
|
case NestedNameSpecifier::Identifier:
|
|
return 0;
|
|
case NestedNameSpecifier::TypeSpec:
|
|
case NestedNameSpecifier::TypeSpecWithTemplate:
|
|
{
|
|
const Type *ntype = nested->getAsType();
|
|
where = ntype->getAsCXXRecordDecl();
|
|
if (!where) return 0;
|
|
break;
|
|
}
|
|
case NestedNameSpecifier::Super:
|
|
// Microsoft's __super::
|
|
return 0;
|
|
};
|
|
}
|
|
} else if (P.getCurToken().is(clang::tok::annot_typename)) {
|
|
// A deduced template?
|
|
|
|
// P.getTypeAnnotation() takes a non-const Token& until clang r306291.
|
|
//auto ParsedTy = P.getTypeAnnotation(P.getCurToken());
|
|
auto ParsedTy
|
|
= ParsedType::getFromOpaquePtr(P.getCurToken().getAnnotationValue());
|
|
if (ParsedTy) {
|
|
QualType QT = ParsedTy.get();
|
|
const Type* TyPtr = QT.getTypePtr();
|
|
if (const auto *LocInfoTy = dyn_cast<LocInfoType>(TyPtr))
|
|
TyPtr = LocInfoTy->getType().getTypePtr();
|
|
TyPtr = TyPtr->getUnqualifiedDesugaredType();
|
|
if (const auto *DTST
|
|
= dyn_cast<DeducedTemplateSpecializationType>(TyPtr)) {
|
|
if (auto TD = DTST->getTemplateName().getAsTemplateDecl()) {
|
|
if (auto CTD = dyn_cast<ClassTemplateDecl>(TD))
|
|
return CTD;
|
|
}
|
|
}
|
|
}
|
|
} else if (P.getCurToken().is(clang::tok::identifier)) {
|
|
// We have a single indentifier, let's look for it in the
|
|
// the global scope.
|
|
where = Context.getTranslationUnitDecl();
|
|
}
|
|
if (where) {
|
|
// Great we now have a scope and something to search for,let's go ahead.
|
|
Interpreter::PushTransactionRAII pushedT(m_Interpreter);
|
|
DeclContext::lookup_result R
|
|
= where->lookup(P.getCurToken().getIdentifierInfo());
|
|
for (DeclContext::lookup_iterator I = R.begin(), E = R.end();
|
|
I != E; ++I) {
|
|
ClassTemplateDecl *theDecl = dyn_cast<ClassTemplateDecl>(*I);
|
|
if (theDecl)
|
|
return theDecl;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const ValueDecl* LookupHelper::findDataMember(const clang::Decl* scopeDecl,
|
|
llvm::StringRef dataName,
|
|
DiagSetting diagOnOff) const {
|
|
// Lookup a data member based on its Decl(Context), name.
|
|
|
|
Parser& P = *m_Parser;
|
|
Sema& S = P.getActions();
|
|
Preprocessor& PP = S.getPreprocessor();
|
|
|
|
IdentifierInfo *dataII = &PP.getIdentifierTable().get(dataName);
|
|
DeclarationName decl_name( dataII );
|
|
|
|
const clang::DeclContext *dc = llvm::cast<clang::DeclContext>(scopeDecl);
|
|
|
|
Interpreter::PushTransactionRAII pushedT(m_Interpreter);
|
|
DeclContext::lookup_result lookup = const_cast<clang::DeclContext*>(dc)->lookup(decl_name);
|
|
for (DeclContext::lookup_iterator I = lookup.begin(), E = lookup.end();
|
|
I != E; ++I) {
|
|
const ValueDecl *result = dyn_cast<ValueDecl>(*I);
|
|
if (result && !isa<FunctionDecl>(result))
|
|
return result;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
DeclContext* getContextAndSpec(CXXScopeSpec &SS,
|
|
const Decl* scopeDecl,
|
|
ASTContext& Context, Sema &S) {
|
|
//
|
|
// Some validity checks on the passed decl.
|
|
//
|
|
DeclContext* foundDC = dyn_cast<DeclContext>(const_cast<Decl*>(scopeDecl));
|
|
if (foundDC->isDependentContext()) {
|
|
// Passed decl is a template, we cannot use it.
|
|
return 0;
|
|
}
|
|
if (scopeDecl->isInvalidDecl()) {
|
|
// if the decl is invalid try to clean up
|
|
UnloadDecl(&S, const_cast<Decl*>(scopeDecl));
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Convert the passed decl into a nested name specifier,
|
|
// a scope spec, and a decl context.
|
|
//
|
|
NestedNameSpecifier* classNNS = 0;
|
|
if (const NamespaceDecl* NSD = dyn_cast<NamespaceDecl>(scopeDecl)) {
|
|
classNNS = NestedNameSpecifier::Create(Context, 0,
|
|
const_cast<NamespaceDecl*>(NSD));
|
|
SS.MakeTrivial(Context, classNNS, scopeDecl->getSourceRange());
|
|
return foundDC;
|
|
}
|
|
else if (const RecordDecl* RD = dyn_cast<RecordDecl>(scopeDecl)) {
|
|
const Type* T = Context.getRecordType(RD).getTypePtr();
|
|
classNNS = NestedNameSpecifier::Create(Context, 0, false, T);
|
|
// We pass a 'random' but valid source range.
|
|
SS.MakeTrivial(Context, classNNS, scopeDecl->getSourceRange());
|
|
if (S.RequireCompleteDeclContext(SS, foundDC)) {
|
|
// Forward decl or instantiation failure, we cannot use it.
|
|
return 0;
|
|
}
|
|
return foundDC;
|
|
}
|
|
else if (llvm::isa<TranslationUnitDecl>(scopeDecl)) {
|
|
// We pass a 'random' but valid source range.
|
|
SS.MakeGlobal(Context,scopeDecl->getLocation());
|
|
return foundDC;
|
|
}
|
|
// Not a namespace or class, we cannot use it.
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
DeclContext* getCompleteContext(const Decl* scopeDecl,
|
|
ASTContext& Context, Sema &S) {
|
|
//
|
|
// Some validity checks on the passed decl.
|
|
//
|
|
DeclContext* foundDC = dyn_cast<DeclContext>(const_cast<Decl*>(scopeDecl));
|
|
if (foundDC->isDependentContext()) {
|
|
// Passed decl is a template, we cannot use it.
|
|
return 0;
|
|
}
|
|
if (scopeDecl->isInvalidDecl()) {
|
|
// if the decl is invalid try to clean up
|
|
UnloadDecl(&S, const_cast<Decl*>(scopeDecl));
|
|
return 0;
|
|
}
|
|
//
|
|
// Convert the passed decl into a nested name specifier,
|
|
// a scope spec, and a decl context.
|
|
//
|
|
if (isa<NamespaceDecl>(scopeDecl)) {
|
|
return foundDC;
|
|
}
|
|
else if (const RecordDecl* RD = dyn_cast<RecordDecl>(scopeDecl)) {
|
|
if (RD->getDefinition()) {
|
|
// We are already complete, we are done.
|
|
return foundDC;
|
|
} else {
|
|
//const Type* T = Context.getRecordType(RD).getTypePtr();
|
|
const Type* T = Context.getTypeDeclType(RD).getTypePtr();
|
|
NestedNameSpecifier* classNNS = NestedNameSpecifier::Create(Context, 0, false, T);
|
|
// We pass a 'random' but valid source range.
|
|
CXXScopeSpec SS;
|
|
SS.MakeTrivial(Context, classNNS, scopeDecl->getSourceRange());
|
|
|
|
if (S.RequireCompleteDeclContext(SS, foundDC)) {
|
|
// Forward decl or instantiation failure, we cannot use it.
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if (llvm::isa<TranslationUnitDecl>(scopeDecl)) {
|
|
return dyn_cast<DeclContext>(const_cast<Decl*>(scopeDecl));
|
|
}
|
|
else {
|
|
// Not a namespace or class, we cannot use it.
|
|
return 0;
|
|
}
|
|
|
|
return foundDC;
|
|
}
|
|
|
|
static bool FuncArgTypesMatch(const ASTContext& C,
|
|
const llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
const FunctionProtoType* FPT) {
|
|
// FIXME: What if FTP->arg_size() != GivenArgTypes.size()?
|
|
FunctionProtoType::param_type_iterator ATI = FPT->param_type_begin();
|
|
FunctionProtoType::param_type_iterator E = FPT->param_type_end();
|
|
llvm::SmallVectorImpl<Expr*>::const_iterator GAI = GivenArgs.begin();
|
|
for (; ATI && (ATI != E); ++ATI, ++GAI) {
|
|
if ((*GAI)->isLValue()) {
|
|
// If the user specified a reference we may have transform it into
|
|
// an LValue non reference (See getExprProto) to have it in a form
|
|
// useful for the lookup. So we are a bit sloppy per se here (maybe)
|
|
const ReferenceType *RefType = (*ATI)->getAs<ReferenceType>();
|
|
if (RefType) {
|
|
if (!C.hasSameType(RefType->getPointeeType(),(*GAI)->getType()))
|
|
return false;
|
|
} else if (!C.hasSameType(*ATI,(*GAI)->getType())) {
|
|
return false;
|
|
}
|
|
} else if (!C.hasSameType(*ATI, (*GAI)->getType() )) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool IsOverload(const ASTContext& C,
|
|
const TemplateArgumentListInfo* FuncTemplateArgs,
|
|
const llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
const FunctionDecl* FD) {
|
|
|
|
//FunctionTemplateDecl* FTD = FD->getDescribedFunctionTemplate();
|
|
QualType FQT = C.getCanonicalType(FD->getType());
|
|
if (llvm::isa<FunctionNoProtoType>(FQT.getTypePtr())) {
|
|
// A K&R-style function (no prototype), is considered to match the args.
|
|
return false;
|
|
}
|
|
const FunctionProtoType* FPT = llvm::cast<FunctionProtoType>(FQT);
|
|
if ((GivenArgs.size() != FPT->getNumParams()) ||
|
|
//(GivenArgsAreEllipsis != FPT->isVariadic()) ||
|
|
!FuncArgTypesMatch(C, GivenArgs, FPT)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static
|
|
const FunctionDecl* overloadFunctionSelector(DeclContext* foundDC,
|
|
bool objectIsConst,
|
|
const llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
LookupResult &Result,
|
|
DeclarationNameInfo &FuncNameInfo,
|
|
const TemplateArgumentListInfo* FuncTemplateArgs,
|
|
ASTContext& Context, Parser &P, Sema &S,
|
|
LookupHelper::DiagSetting diagOnOff) {
|
|
//
|
|
// Our return value.
|
|
//
|
|
FunctionDecl* TheDecl = 0;
|
|
|
|
//
|
|
// If we are looking up a member function, construct
|
|
// the implicit object argument.
|
|
//
|
|
// Note: For now this is always a non-CV qualified lvalue.
|
|
//
|
|
QualType ClassType;
|
|
Expr::Classification ObjExprClassification;
|
|
if (CXXRecordDecl* CRD = dyn_cast<CXXRecordDecl>(foundDC)) {
|
|
if (objectIsConst)
|
|
ClassType = Context.getTypeDeclType(CRD).getCanonicalType().withConst();
|
|
else ClassType = Context.getTypeDeclType(CRD).getCanonicalType();
|
|
OpaqueValueExpr ObjExpr(SourceLocation(),
|
|
ClassType, VK_LValue);
|
|
ObjExprClassification = ObjExpr.Classify(Context);
|
|
}
|
|
|
|
//
|
|
// Tell the diagnostic engine to ignore all diagnostics.
|
|
//
|
|
bool OldSuppressAllDiagnostics
|
|
= S.getDiagnostics().getSuppressAllDiagnostics();
|
|
S.getDiagnostics().setSuppressAllDiagnostics(
|
|
diagOnOff == LookupHelper::NoDiagnostics);
|
|
|
|
struct ResetDiagSuppression {
|
|
bool _Old;
|
|
Sema& _S;
|
|
ResetDiagSuppression(Sema &S, bool Old): _Old(Old), _S(S) {}
|
|
~ResetDiagSuppression() {
|
|
_S.getDiagnostics().setSuppressAllDiagnostics(_Old);
|
|
}
|
|
} DiagSuppressionRAII(S, OldSuppressAllDiagnostics);
|
|
|
|
//
|
|
// Construct the overload candidate set.
|
|
//
|
|
OverloadCandidateSet Candidates(FuncNameInfo.getLoc(),
|
|
OverloadCandidateSet::CSK_Normal);
|
|
for (LookupResult::iterator I = Result.begin(), E = Result.end();
|
|
I != E; ++I) {
|
|
NamedDecl* ND = *I;
|
|
if (FunctionDecl* FD = dyn_cast<FunctionDecl>(ND)) {
|
|
if (isa<CXXMethodDecl>(FD) &&
|
|
!cast<CXXMethodDecl>(FD)->isStatic() &&
|
|
!isa<CXXConstructorDecl>(FD)) {
|
|
// Class method, not static, not a constructor, so has
|
|
// an implicit object argument.
|
|
CXXMethodDecl* MD = cast<CXXMethodDecl>(FD);
|
|
if (FuncTemplateArgs && (FuncTemplateArgs->size() != 0)) {
|
|
// Explicit template args were given, cannot use a plain func.
|
|
continue;
|
|
}
|
|
S.AddMethodCandidate(MD, I.getPair(), MD->getParent(),
|
|
/*ObjectType=*/ClassType,
|
|
/*ObjectClassification=*/ObjExprClassification,
|
|
llvm::makeArrayRef<Expr*>(GivenArgs.data(), GivenArgs.size()),
|
|
Candidates);
|
|
}
|
|
else {
|
|
const FunctionProtoType* Proto = dyn_cast<FunctionProtoType>(
|
|
FD->getType()->getAs<clang::FunctionType>());
|
|
if (!Proto) {
|
|
// Function has no prototype, cannot do overloading.
|
|
continue;
|
|
}
|
|
if (FuncTemplateArgs && (FuncTemplateArgs->size() != 0)) {
|
|
// Explicit template args were given, cannot use a plain func.
|
|
continue;
|
|
}
|
|
S.AddOverloadCandidate(FD, I.getPair(),
|
|
llvm::makeArrayRef<Expr*>(GivenArgs.data(), GivenArgs.size()),
|
|
Candidates);
|
|
}
|
|
}
|
|
else if (FunctionTemplateDecl* FTD =
|
|
dyn_cast<FunctionTemplateDecl>(ND)) {
|
|
if (isa<CXXMethodDecl>(FTD->getTemplatedDecl()) &&
|
|
!cast<CXXMethodDecl>(FTD->getTemplatedDecl())->isStatic() &&
|
|
!isa<CXXConstructorDecl>(FTD->getTemplatedDecl())) {
|
|
// Class method template, not static, not a constructor, so has
|
|
// an implicit object argument.
|
|
S.AddMethodTemplateCandidate(FTD, I.getPair(),
|
|
cast<CXXRecordDecl>(FTD->getDeclContext()),
|
|
const_cast<TemplateArgumentListInfo*>(FuncTemplateArgs),
|
|
/*ObjectType=*/ClassType,
|
|
/*ObjectClassification=*/ObjExprClassification,
|
|
llvm::makeArrayRef<Expr*>(GivenArgs.data(), GivenArgs.size()),
|
|
Candidates);
|
|
}
|
|
else {
|
|
S.AddTemplateOverloadCandidate(FTD, I.getPair(),
|
|
const_cast<TemplateArgumentListInfo*>(FuncTemplateArgs),
|
|
llvm::makeArrayRef<Expr*>(GivenArgs.data(), GivenArgs.size()),
|
|
Candidates, /*SuppressUserConversions=*/false);
|
|
}
|
|
}
|
|
else {
|
|
// Is there any other cases?
|
|
}
|
|
}
|
|
//
|
|
// Find the best viable function from the set.
|
|
//
|
|
{
|
|
OverloadCandidateSet::iterator Best;
|
|
OverloadingResult OR = Candidates.BestViableFunction(S,
|
|
Result.getNameLoc(),
|
|
Best);
|
|
if (OR == OR_Success) {
|
|
TheDecl = Best->Function;
|
|
// We prefer to get the canonical decl for consistency and ease
|
|
// of comparison.
|
|
TheDecl = TheDecl->getCanonicalDecl();
|
|
if (TheDecl->isTemplateInstantiation() && !TheDecl->isDefined()) {
|
|
S.InstantiateFunctionDefinition(SourceLocation(), TheDecl,
|
|
true /*recursive instantiation*/);
|
|
}
|
|
if (TheDecl->isInvalidDecl()) {
|
|
// if the decl is invalid try to clean up
|
|
UnloadDecl(&S, const_cast<FunctionDecl*>(TheDecl));
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return TheDecl;
|
|
}
|
|
|
|
static
|
|
const FunctionDecl* matchFunctionSelector(DeclContext* foundDC,
|
|
bool objectIsConst,
|
|
const llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
LookupResult &Result,
|
|
DeclarationNameInfo &FuncNameInfo,
|
|
const TemplateArgumentListInfo* FuncTemplateArgs,
|
|
ASTContext& Context, Parser &P, Sema &S,
|
|
LookupHelper::DiagSetting diagOnOff) {
|
|
//
|
|
// Our return value.
|
|
//
|
|
const FunctionDecl* TheDecl = overloadFunctionSelector(foundDC, objectIsConst,
|
|
GivenArgs, Result,
|
|
FuncNameInfo,
|
|
FuncTemplateArgs,
|
|
Context,P,S,
|
|
diagOnOff);
|
|
|
|
if (TheDecl) {
|
|
if ( IsOverload(Context, FuncTemplateArgs, GivenArgs, TheDecl) ) {
|
|
return 0;
|
|
} else {
|
|
// Double check const-ness.
|
|
if (const clang::CXXMethodDecl *md =
|
|
llvm::dyn_cast<clang::CXXMethodDecl>(TheDecl)) {
|
|
if (md->getMethodQualifiers().hasConst()) {
|
|
if (!objectIsConst) {
|
|
TheDecl = 0;
|
|
}
|
|
} else { // FIXME: The else should be attached to the if hasConst stmt
|
|
if (objectIsConst) {
|
|
TheDecl = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TheDecl;
|
|
}
|
|
|
|
static bool ParseWithShortcuts(DeclContext* foundDC, ASTContext& Context,
|
|
llvm::StringRef funcName,
|
|
Interpreter* Interp,
|
|
UnqualifiedId &FuncId,
|
|
LookupHelper::DiagSetting diagOnOff,
|
|
ParserStateRAII &ResetParserState) {
|
|
|
|
// Use a very simple parse step that dectect whether the name search (which
|
|
// is already supposed to be an unqualified name) is a simple identifier,
|
|
// a constructor name or a destructor name. In those 3 cases, we can easily
|
|
// create the UnqualifiedId object that would have resulted from the 'real'
|
|
// parse. By using this direct creation of the UnqualifiedId, we avoid the
|
|
// 'permanent' cost associated with creating a memory buffer and the
|
|
// associated FileID.
|
|
|
|
// If the name is a template or an operator, we revert to the regular parse
|
|
// (and its associated permanent cost).
|
|
|
|
// In the operator case, the additional work is in the case of a conversion
|
|
// operator where we would need to 'quickly' parse the type itself (if want
|
|
// to avoid the permanent cost).
|
|
|
|
// In the case with the template the problem gets a bit worse as we need to
|
|
// handle potentially arbitrary spaces and ordering
|
|
// ('const int' vs 'int const', etc.)
|
|
|
|
Parser &P = const_cast<Parser&>(Interp->getParser());
|
|
Sema &S = Interp->getSema();
|
|
if (funcName.size() == 0) return false;
|
|
Preprocessor& PP = S.getPreprocessor();
|
|
|
|
// See if we can avoid creating the buffer, for now we just look for
|
|
// simple indentifier, constructor and destructor.
|
|
|
|
|
|
if (funcName.size() > 8 && strncmp(funcName.data(),"operator",8) == 0
|
|
&&( funcName[8] == ' ' || funcName[8] == '*'
|
|
|| funcName[8] == '%' || funcName[8] == '&'
|
|
|| funcName[8] == '|' || funcName[8] == '/'
|
|
|| funcName[8] == '+' || funcName[8] == '-'
|
|
|| funcName[8] == '(' || funcName[8] == '['
|
|
|| funcName[8] == '=' || funcName[8] == '!'
|
|
|| funcName[8] == '<' || funcName[8] == '>'
|
|
|| funcName[8] == '-' || funcName[8] == '^')
|
|
) {
|
|
// We have called:
|
|
// setOperatorFunctionId (SourceLocation OperatorLoc,
|
|
// OverloadedOperatorKind Op,
|
|
// SourceLocation SymbolLocations[3])
|
|
// or
|
|
// setConversionFunctionId (SourceLocation OperatorLoc,
|
|
// ParsedType Ty, SourceLocation EndLoc)
|
|
} else if (funcName.find('<') != StringRef::npos) {
|
|
// We might have a template name,
|
|
// setTemplateId (TemplateIdAnnotation *TemplateId)
|
|
// or
|
|
// setConstructorTemplateId (TemplateIdAnnotation *TemplateId)
|
|
} else if (funcName[0] == '~') {
|
|
// Destructor.
|
|
// Let's see if this is our contructor.
|
|
TagDecl *decl = llvm::dyn_cast<TagDecl>(foundDC);
|
|
if (decl) {
|
|
// We have a class or struct or something.
|
|
if (funcName.substr(1).equals(decl->getName())) {
|
|
ParsedType PT;
|
|
QualType QT( decl->getTypeForDecl(), 0 );
|
|
PT.set(QT);
|
|
FuncId.setDestructorName(SourceLocation(),PT,SourceLocation());
|
|
return true;
|
|
}
|
|
}
|
|
// So it starts with ~ but is not followed by the name of
|
|
// a class or at least not the one that is the declaration context,
|
|
// let's try a real parsing, to see if we can do better.
|
|
} else {
|
|
// We either have a simple type or a constructor name
|
|
TagDecl *decl = llvm::dyn_cast<TagDecl>(foundDC);
|
|
if (decl) {
|
|
// We have a class or struct or something.
|
|
if (funcName.equals(decl->getName())) {
|
|
ParsedType PT;
|
|
QualType QT( decl->getTypeForDecl(), 0 );
|
|
PT.set(QT);
|
|
FuncId.setConstructorName(PT,SourceLocation(),SourceLocation());
|
|
} else {
|
|
IdentifierInfo *TypeInfoII = &PP.getIdentifierTable().get(funcName);
|
|
FuncId.setIdentifier (TypeInfoII, SourceLocation() );
|
|
}
|
|
return true;
|
|
} else {
|
|
// We have a namespace like context, it can't be a constructor
|
|
IdentifierInfo *TypeInfoII = &PP.getIdentifierTable().get(funcName);
|
|
FuncId.setIdentifier (TypeInfoII, SourceLocation() );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Setup to reparse as a type.
|
|
//
|
|
//
|
|
// Create a fake file to parse the function name.
|
|
//
|
|
// FIXME:, TODO: Cleanup that complete mess.
|
|
ResetParserState.SetSkipToEOF(true);
|
|
{
|
|
PP.getDiagnostics().setSuppressAllDiagnostics(diagOnOff ==
|
|
LookupHelper::NoDiagnostics);
|
|
std::unique_ptr<llvm::MemoryBuffer>
|
|
SB(llvm::MemoryBuffer::getMemBufferCopy(funcName.str() + "\n",
|
|
"lookup.funcname.file"));
|
|
SourceLocation NewLoc = Interp->getNextAvailableLoc();
|
|
FileID FID = S.getSourceManager().createFileID(std::move(SB),
|
|
SrcMgr::C_User,
|
|
/*LoadedID*/0,
|
|
/*LoadedOffset*/0, NewLoc);
|
|
PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc);
|
|
PP.Lex(const_cast<clang::Token&>(P.getCurToken()));
|
|
}
|
|
|
|
|
|
//
|
|
// Parse the function name.
|
|
//
|
|
SourceLocation TemplateKWLoc;
|
|
CXXScopeSpec SS;
|
|
{
|
|
Decl *decl = llvm::dyn_cast<Decl>(foundDC);
|
|
getContextAndSpec(SS,decl,Context,S);
|
|
}
|
|
if (P.ParseUnqualifiedId(SS, ParsedType(),
|
|
/*ObjectHadErrors=*/false,
|
|
/*EnteringContext*/false,
|
|
/*AllowDestructorName*/true,
|
|
/*AllowConstructorName*/true,
|
|
/*AllowDeductionGuide*/ false,
|
|
&TemplateKWLoc,
|
|
FuncId)) {
|
|
// Failed parse, cleanup.
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
T findFunction(DeclContext* foundDC,
|
|
llvm::StringRef funcName,
|
|
const llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
bool objectIsConst,
|
|
ASTContext& Context, Interpreter* Interp,
|
|
T (*functionSelector)(DeclContext* foundDC,
|
|
bool objectIsConst,
|
|
const llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
LookupResult &Result,
|
|
DeclarationNameInfo &FuncNameInfo,
|
|
const TemplateArgumentListInfo* FuncTemplateArgs,
|
|
ASTContext& Context, Parser &P, Sema &S,
|
|
LookupHelper::DiagSetting diagOnOff),
|
|
LookupHelper::DiagSetting diagOnOff
|
|
) {
|
|
// Given the correctly types arguments, etc. find the function itself.
|
|
|
|
//
|
|
// Make the class we are looking up the function
|
|
// in the current scope to please the constructor
|
|
// name lookup. We do not need to do this otherwise,
|
|
// and may be able to remove it in the future if
|
|
// the way constructors are looked up changes.
|
|
//
|
|
Parser &P = const_cast<Parser&>(Interp->getParser());
|
|
Sema &S = Interp->getSema();
|
|
DeclContext* OldEntity = P.getCurScope()->getEntity();
|
|
DeclContext* TUCtx = Context.getTranslationUnitDecl();
|
|
P.getCurScope()->setEntity(TUCtx);
|
|
P.EnterScope(Scope::DeclScope);
|
|
P.getCurScope()->setEntity(foundDC);
|
|
P.EnterScope(Scope::DeclScope);
|
|
Sema::ContextRAII SemaContext(S, foundDC);
|
|
S.EnterDeclaratorContext(P.getCurScope(), foundDC);
|
|
|
|
UnqualifiedId FuncId;
|
|
ParserStateRAII ResetParserState(P, false /*skipToEOF*/);
|
|
if (!ParseWithShortcuts(foundDC, Context, funcName, Interp, FuncId,
|
|
diagOnOff, ResetParserState)) {
|
|
// Failed parse, cleanup.
|
|
// Destroy the scope we created first, and
|
|
// restore the original.
|
|
S.ExitDeclaratorContext(P.getCurScope());
|
|
P.ExitScope();
|
|
P.ExitScope();
|
|
P.getCurScope()->setEntity(OldEntity);
|
|
// Then exit.
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Get any template args in the function name.
|
|
//
|
|
TemplateArgumentListInfo FuncTemplateArgsBuffer;
|
|
DeclarationNameInfo FuncNameInfo;
|
|
const TemplateArgumentListInfo* FuncTemplateArgs;
|
|
S.DecomposeUnqualifiedId(FuncId, FuncTemplateArgsBuffer, FuncNameInfo,
|
|
FuncTemplateArgs);
|
|
|
|
//
|
|
// Lookup the function name in the given class now.
|
|
//
|
|
DeclarationName FuncName = FuncNameInfo.getName();
|
|
SourceLocation FuncNameLoc = FuncNameInfo.getLoc();
|
|
LookupResult Result(S, FuncName, FuncNameLoc, Sema::LookupMemberName,
|
|
Sema::NotForRedeclaration);
|
|
Result.suppressDiagnostics();
|
|
|
|
bool LookupSuccess = true;
|
|
if (FuncTemplateArgsBuffer.size()) {
|
|
// It's a template. Calculate the NNS and do qualified template lookup.
|
|
NestedNameSpecifier* scopeNNS = nullptr;
|
|
SourceRange scopeSrcRange;
|
|
if (isa<TranslationUnitDecl>(foundDC)) {
|
|
scopeNNS = NestedNameSpecifier::GlobalSpecifier(Context);
|
|
} else if (const auto *foundNS = dyn_cast<NamespaceDecl>(foundDC)) {
|
|
scopeNNS = NestedNameSpecifier::Create(Context, /*NNSPrefix*/ nullptr,
|
|
foundNS);
|
|
scopeSrcRange = foundNS->getSourceRange();
|
|
} else if (const auto *foundRD = dyn_cast<RecordDecl>(foundDC)) {
|
|
// a type
|
|
const Type* foundTy = Context.getTypeDeclType(foundRD).getTypePtr();
|
|
scopeNNS = NestedNameSpecifier::Create(Context, /*NNSPrefix*/ nullptr,
|
|
/*Template*/ false, foundTy);
|
|
scopeSrcRange = foundRD->getSourceRange();
|
|
}
|
|
CXXScopeSpec SS;
|
|
if (scopeNNS)
|
|
SS.MakeTrivial(Context, scopeNNS, scopeSrcRange);
|
|
bool MemberOfUnknownSpecialization;
|
|
S.LookupTemplateName(Result, P.getCurScope(), SS, QualType(),
|
|
/*EnteringContext*/false,
|
|
MemberOfUnknownSpecialization);
|
|
// "Translation" of the TemplateDecl to the specialization is done
|
|
// in findAnyFunctionSelector() given the ExplicitTemplateArgs.
|
|
if (Result.empty())
|
|
LookupSuccess = false;
|
|
} else {
|
|
LookupSuccess = S.LookupQualifiedName(Result, foundDC);
|
|
}
|
|
|
|
if (!LookupSuccess) {
|
|
// Lookup failed.
|
|
// Destroy the scope we created first, and
|
|
// restore the original.
|
|
S.ExitDeclaratorContext(P.getCurScope());
|
|
P.ExitScope();
|
|
P.ExitScope();
|
|
P.getCurScope()->setEntity(OldEntity);
|
|
// Then cleanup and exit.
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Destroy the scope we created, and restore the original.
|
|
//
|
|
S.ExitDeclaratorContext(P.getCurScope());
|
|
P.ExitScope();
|
|
P.ExitScope();
|
|
P.getCurScope()->setEntity(OldEntity);
|
|
//
|
|
// Check for lookup failure.
|
|
//
|
|
if (Result.getResultKind() != LookupResult::Found &&
|
|
Result.getResultKind() != LookupResult::FoundOverloaded) {
|
|
// Lookup failed.
|
|
return 0;
|
|
}
|
|
return functionSelector(foundDC,objectIsConst,GivenArgs,
|
|
Result,
|
|
FuncNameInfo,
|
|
FuncTemplateArgs,
|
|
Context, P, S, diagOnOff);
|
|
}
|
|
|
|
template <typename DigestArgsInput, typename returnType>
|
|
returnType execFindFunction(Parser &P,
|
|
Interpreter* Interp,
|
|
LookupHelper &LH,
|
|
const clang::Decl* scopeDecl,
|
|
llvm::StringRef funcName,
|
|
const typename DigestArgsInput::ArgsInput &funcArgs,
|
|
bool objectIsConst,
|
|
returnType (*functionSelector)(DeclContext* foundDC,
|
|
bool objectIsConst,
|
|
const llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
LookupResult &Result,
|
|
DeclarationNameInfo &FuncNameInfo,
|
|
const TemplateArgumentListInfo* FuncTemplateArgs,
|
|
ASTContext& Context, Parser &P, Sema &S,
|
|
LookupHelper::DiagSetting diagOnOff),
|
|
LookupHelper::DiagSetting diagOnOff
|
|
)
|
|
{
|
|
|
|
assert(scopeDecl && "Decl cannot be null");
|
|
//
|
|
// Some utilities.
|
|
//
|
|
Sema& S = P.getActions();
|
|
ASTContext& Context = S.getASTContext();
|
|
|
|
//
|
|
// Convert the passed decl into a nested name specifier,
|
|
// a scope spec, and a decl context.
|
|
//
|
|
// Do this 'early' to save on the expansive parser setup,
|
|
// in case of failure.
|
|
//
|
|
DeclContext* foundDC = getCompleteContext(scopeDecl,Context,S);
|
|
if (!foundDC) return 0;
|
|
|
|
DigestArgsInput inputEval;
|
|
llvm::SmallVector<Expr*, 4> GivenArgs;
|
|
if (!inputEval(GivenArgs,funcArgs,diagOnOff,P,Interp,LH)) return 0;
|
|
|
|
Interpreter::PushTransactionRAII pushedT(Interp);
|
|
return findFunction(foundDC,
|
|
funcName, GivenArgs, objectIsConst,
|
|
Context, Interp, functionSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
struct NoParse {
|
|
|
|
typedef const char* ArgsInput;
|
|
|
|
bool operator()(llvm::SmallVectorImpl<Expr*> & /* GivenArgs */,
|
|
const ArgsInput &/* funcArgs */,
|
|
LookupHelper::DiagSetting /* diagOnOff */,
|
|
Parser & /* P */, const Interpreter* /* Interp */,
|
|
const LookupHelper& /* LH */)
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct ExprFromTypes {
|
|
|
|
typedef llvm::SmallVectorImpl<QualType> ArgsInput;
|
|
|
|
llvm::SmallVector<ExprAlloc, 4> ExprMemory;
|
|
|
|
bool operator()(llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
const ArgsInput &GivenTypes,
|
|
LookupHelper::DiagSetting /* diagOnOff */,
|
|
Parser & /* P */, const Interpreter* /* Interp */,
|
|
LookupHelper& /* LH */) {
|
|
|
|
if (GivenTypes.empty()) return true;
|
|
else return getExprProto(GivenArgs,GivenTypes);
|
|
}
|
|
|
|
bool getExprProto(llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
const llvm::SmallVectorImpl<QualType> &GivenTypes) {
|
|
//
|
|
// Create the array of Expr from the array of Types.
|
|
//
|
|
assert(!ExprMemory.size() && "Size must be 0");
|
|
ExprMemory.resize(GivenTypes.size() + 1);
|
|
for(size_t i = 0, e = GivenTypes.size(); i < e; ++i) {
|
|
const clang::QualType QT = GivenTypes[i].getCanonicalType();
|
|
{
|
|
ExprValueKind VK = VK_PRValue;
|
|
if (QT->getAs<LValueReferenceType>()) {
|
|
VK = VK_LValue;
|
|
}
|
|
clang::QualType NonRefQT(QT.getNonReferenceType());
|
|
Expr* val = new (&ExprMemory[i]) OpaqueValueExpr(SourceLocation(),
|
|
NonRefQT, VK);
|
|
GivenArgs.push_back(val);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct ParseProto {
|
|
|
|
typedef llvm::StringRef ArgsInput;
|
|
|
|
llvm::SmallVector<ExprAlloc, 4> ExprMemory;
|
|
|
|
bool operator()(llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
const ArgsInput &funcProto,
|
|
LookupHelper::DiagSetting diagOnOff,
|
|
Parser& P, const Interpreter* Interp,
|
|
LookupHelper& LH) {
|
|
|
|
if (funcProto.empty()) return true;
|
|
else return Parse(GivenArgs,funcProto,diagOnOff, P, Interp, LH);
|
|
}
|
|
|
|
bool Parse(llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
const ArgsInput &funcProto,
|
|
LookupHelper::DiagSetting diagOnOff,
|
|
Parser& P, const Interpreter* Interp,
|
|
LookupHelper& LH) {
|
|
|
|
//
|
|
// Parse the prototype now.
|
|
//
|
|
|
|
StartParsingRAII ParseStarted(LH, funcProto,
|
|
llvm::StringRef("func.prototype.file"),
|
|
diagOnOff);
|
|
|
|
unsigned int nargs = 0;
|
|
while (P.getCurToken().isNot(tok::eof)) {
|
|
TypeResult Res(P.ParseTypeName());
|
|
if (!Res.isUsable()) {
|
|
// Bad parse, done.
|
|
return false;
|
|
}
|
|
TypeSourceInfo *TSI = 0;
|
|
clang::QualType QT = clang::Sema::GetTypeFromParser(Res.get(), &TSI);
|
|
QT = QT.getCanonicalType();
|
|
{
|
|
ExprValueKind VK = VK_PRValue;
|
|
if (QT->getAs<LValueReferenceType>()) {
|
|
VK = VK_LValue;
|
|
}
|
|
// FIXME: This is potentially dangerous because if the capacity exceeds
|
|
// the reserved capacity of ExprMemory, it will reallocate and cause
|
|
// memory corruption on the OpaqueValueExpr. See ROOT-7749.
|
|
clang::QualType NonRefQT(QT.getNonReferenceType());
|
|
ExprMemory.resize(++nargs);
|
|
new (&ExprMemory[nargs-1]) OpaqueValueExpr(TSI->getTypeLoc().getBeginLoc(),
|
|
NonRefQT, VK);
|
|
}
|
|
// Type names should be comma separated.
|
|
// FIXME: Here if we have type followed by name won't work. Eg int f, ...
|
|
if (!P.getCurToken().is(clang::tok::comma)) {
|
|
break;
|
|
}
|
|
// Eat the comma.
|
|
P.ConsumeToken();
|
|
}
|
|
for(unsigned int slot = 0; slot < nargs; ++slot) {
|
|
Expr* val = (OpaqueValueExpr*)( &ExprMemory[slot] );
|
|
GivenArgs.push_back(val);
|
|
}
|
|
if (P.getCurToken().isNot(tok::eof)) {
|
|
// We did not consume all of the prototype, bad parse.
|
|
return false;
|
|
}
|
|
//
|
|
// Cleanup after prototype parse.
|
|
//
|
|
P.SkipUntil(clang::tok::eof);
|
|
// Doesn't reset the diagnostic mappings
|
|
Sema& S = P.getActions();
|
|
S.getDiagnostics().Reset(/*soft=*/true);
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
static
|
|
const FunctionTemplateDecl* findFunctionTemplateSelector(DeclContext* ,
|
|
bool /* objectIsConst */,
|
|
const llvm::SmallVectorImpl<Expr*> &,
|
|
LookupResult &Result,
|
|
DeclarationNameInfo &,
|
|
const TemplateArgumentListInfo* ExplicitTemplateArgs,
|
|
ASTContext&, Parser &,
|
|
Sema &S,
|
|
LookupHelper::DiagSetting diagOnOff) {
|
|
//
|
|
// Check for lookup failure.
|
|
//
|
|
if (Result.empty())
|
|
return 0;
|
|
if (Result.isSingleResult())
|
|
return dyn_cast<FunctionTemplateDecl>(Result.getFoundDecl());
|
|
else {
|
|
for (LookupResult::iterator I = Result.begin(), E = Result.end();
|
|
I != E; ++I) {
|
|
NamedDecl* ND = *I;
|
|
FunctionTemplateDecl *MethodTmpl =dyn_cast<FunctionTemplateDecl>(ND);
|
|
if (MethodTmpl) {
|
|
return MethodTmpl;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const FunctionTemplateDecl*
|
|
LookupHelper::findFunctionTemplate(const clang::Decl* scopeDecl,
|
|
llvm::StringRef templateName,
|
|
DiagSetting diagOnOff,
|
|
bool objectIsConst) const {
|
|
// Lookup a function template based on its Decl(Context), name.
|
|
|
|
return execFindFunction<NoParse>(*m_Parser, m_Interpreter,
|
|
const_cast<LookupHelper&>(*this),
|
|
scopeDecl,
|
|
templateName, "",
|
|
objectIsConst,
|
|
findFunctionTemplateSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
static
|
|
const FunctionDecl* findAnyFunctionSelector(DeclContext* ,
|
|
bool /* objectIsConst */,
|
|
const llvm::SmallVectorImpl<Expr*> &,
|
|
LookupResult &Result,
|
|
DeclarationNameInfo &,
|
|
const TemplateArgumentListInfo* ExplicitTemplateArgs,
|
|
ASTContext&, Parser &, Sema &S,
|
|
LookupHelper::DiagSetting diagOnOff) {
|
|
//
|
|
// Check for lookup failure.
|
|
//
|
|
if (Result.empty())
|
|
return 0;
|
|
if (Result.isSingleResult())
|
|
return dyn_cast<FunctionDecl>(Result.getFoundDecl());
|
|
else {
|
|
NamedDecl *aResult = *(Result.begin());
|
|
FunctionDecl *res = dyn_cast<FunctionDecl>(aResult);
|
|
if (res) return res;
|
|
FunctionTemplateDecl *MethodTmpl =dyn_cast<FunctionTemplateDecl>(aResult);
|
|
if (MethodTmpl) {
|
|
if (!ExplicitTemplateArgs || ExplicitTemplateArgs->size()==0) {
|
|
// Not argument was specified, any instantiation will do.
|
|
|
|
if (MethodTmpl->spec_begin() != MethodTmpl->spec_end()) {
|
|
return *( MethodTmpl->spec_begin() );
|
|
}
|
|
}
|
|
// pick a specialization that result match the given arguments
|
|
SourceLocation loc;
|
|
sema::TemplateDeductionInfo Info(loc);
|
|
FunctionDecl *fdecl = 0;
|
|
Sema::TemplateDeductionResult TemplDedResult
|
|
= S.DeduceTemplateArguments(MethodTmpl,
|
|
const_cast<TemplateArgumentListInfo*>(ExplicitTemplateArgs),
|
|
fdecl,
|
|
Info);
|
|
if (TemplDedResult != Sema::TDK_Success) {
|
|
// Deduction failure.
|
|
return 0;
|
|
} else {
|
|
// Instantiate the function if needed.
|
|
if (!fdecl->isDefined())
|
|
S.InstantiateFunctionDefinition(loc, fdecl,
|
|
true /*recursive instantiation*/);
|
|
if (fdecl->isInvalidDecl()) {
|
|
// if the decl is invalid try to clean up
|
|
UnloadDecl(&S, fdecl);
|
|
return 0;
|
|
}
|
|
return fdecl;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const FunctionDecl* LookupHelper::findAnyFunction(const clang::Decl*scopeDecl,
|
|
llvm::StringRef funcName,
|
|
DiagSetting diagOnOff,
|
|
bool objectIsConst) const {
|
|
|
|
return execFindFunction<NoParse>(*m_Parser, m_Interpreter,
|
|
const_cast<LookupHelper&>(*this),
|
|
scopeDecl,
|
|
funcName, "",
|
|
objectIsConst,
|
|
findAnyFunctionSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
const FunctionDecl*
|
|
LookupHelper::findFunctionProto(const Decl* scopeDecl,
|
|
llvm::StringRef funcName,
|
|
const llvm::SmallVectorImpl<QualType>& funcProto,
|
|
DiagSetting diagOnOff, bool objectIsConst) const {
|
|
assert(scopeDecl && "Decl cannot be null");
|
|
|
|
return execFindFunction<ExprFromTypes>(*m_Parser, m_Interpreter,
|
|
const_cast<LookupHelper&>(*this),
|
|
scopeDecl,
|
|
funcName,
|
|
funcProto,
|
|
objectIsConst,
|
|
overloadFunctionSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
const FunctionDecl* LookupHelper::findFunctionProto(const Decl* scopeDecl,
|
|
llvm::StringRef funcName,
|
|
llvm::StringRef funcProto,
|
|
DiagSetting diagOnOff,
|
|
bool objectIsConst) const{
|
|
assert(scopeDecl && "Decl cannot be null");
|
|
|
|
return execFindFunction<ParseProto>(*m_Parser, m_Interpreter,
|
|
const_cast<LookupHelper&>(*this),
|
|
scopeDecl,
|
|
funcName,
|
|
funcProto,
|
|
objectIsConst,
|
|
overloadFunctionSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
const FunctionDecl*
|
|
LookupHelper::matchFunctionProto(const Decl* scopeDecl,
|
|
llvm::StringRef funcName,
|
|
llvm::StringRef funcProto,
|
|
DiagSetting diagOnOff,
|
|
bool objectIsConst) const {
|
|
assert(scopeDecl && "Decl cannot be null");
|
|
|
|
return execFindFunction<ParseProto>(*m_Parser, m_Interpreter,
|
|
const_cast<LookupHelper&>(*this),
|
|
scopeDecl,
|
|
funcName,
|
|
funcProto,
|
|
objectIsConst,
|
|
matchFunctionSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
const FunctionDecl*
|
|
LookupHelper::matchFunctionProto(const Decl* scopeDecl,
|
|
llvm::StringRef funcName,
|
|
const llvm::SmallVectorImpl<QualType>& funcProto,
|
|
DiagSetting diagOnOff,
|
|
bool objectIsConst) const {
|
|
assert(scopeDecl && "Decl cannot be null");
|
|
|
|
return execFindFunction<ExprFromTypes>(*m_Parser, m_Interpreter,
|
|
const_cast<LookupHelper&>(*this),
|
|
scopeDecl,
|
|
funcName,
|
|
funcProto,
|
|
objectIsConst,
|
|
matchFunctionSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
struct ParseArgs {
|
|
|
|
typedef llvm::StringRef ArgsInput;
|
|
|
|
bool operator()(llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
const ArgsInput &funcArgs,
|
|
LookupHelper::DiagSetting diagOnOff,
|
|
Parser &P, const Interpreter* Interp,
|
|
LookupHelper& LH) {
|
|
|
|
if (funcArgs.empty()) return true;
|
|
else return Parse(GivenArgs,funcArgs,diagOnOff, P, Interp, LH);
|
|
}
|
|
|
|
bool Parse(llvm::SmallVectorImpl<Expr*> &GivenArgs,
|
|
llvm::StringRef funcArgs,
|
|
LookupHelper::DiagSetting diagOnOff,
|
|
Parser &P, const Interpreter* Interp,
|
|
LookupHelper& LH) {
|
|
|
|
//
|
|
// Parse the arguments now.
|
|
//
|
|
|
|
Interpreter::PushTransactionRAII TforDeser(Interp);
|
|
StartParsingRAII ParseStarted(LH, funcArgs,
|
|
llvm::StringRef("func.args.file"),
|
|
diagOnOff);
|
|
|
|
Sema& S = P.getActions();
|
|
ASTContext& Context = S.getASTContext();
|
|
|
|
PrintingPolicy Policy(Context.getPrintingPolicy());
|
|
Policy.SuppressTagKeyword = true;
|
|
Policy.SuppressUnwrittenScope = true;
|
|
Policy.SuppressInitializers = true;
|
|
Policy.AnonymousTagLocations = false;
|
|
std::string proto;
|
|
{
|
|
bool first_time = true;
|
|
while (P.getCurToken().isNot(tok::eof)) {
|
|
ExprResult Res = P.ParseAssignmentExpression();
|
|
if (Res.isUsable()) {
|
|
Expr* expr = Res.get();
|
|
GivenArgs.push_back(expr);
|
|
if (first_time) {
|
|
first_time = false;
|
|
}
|
|
else {
|
|
proto += ',';
|
|
}
|
|
stdstrstream tmp;
|
|
expr->printPretty(tmp, /*PrinterHelper=*/0, Policy,
|
|
/*Indentation=*/0);
|
|
proto += tmp.str();
|
|
}
|
|
if (!P.getCurToken().is(tok::comma)) {
|
|
break;
|
|
}
|
|
P.ConsumeToken();
|
|
}
|
|
}
|
|
// For backward compatibility with CINT accept (for now?) a trailing close
|
|
// parenthesis.
|
|
if (P.getCurToken().isNot(tok::eof) && P.getCurToken().isNot(tok::r_paren) ) {
|
|
// We did not consume all of the arg list, bad parse.
|
|
return false;
|
|
}
|
|
//
|
|
// Cleanup after the arg list parse.
|
|
//
|
|
P.SkipUntil(clang::tok::eof);
|
|
// Doesn't reset the diagnostic mappings
|
|
S.getDiagnostics().Reset(/*soft=*/true);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
const FunctionDecl*
|
|
LookupHelper::findFunctionArgs(const Decl* scopeDecl,
|
|
llvm::StringRef funcName,
|
|
llvm::StringRef funcArgs,
|
|
DiagSetting diagOnOff,
|
|
bool objectIsConst) const {
|
|
assert(scopeDecl && "Decl cannot be null");
|
|
|
|
return execFindFunction<ParseArgs>(*m_Parser, m_Interpreter,
|
|
const_cast<LookupHelper&>(*this),
|
|
scopeDecl,
|
|
funcName,
|
|
funcArgs,
|
|
objectIsConst,
|
|
overloadFunctionSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
void LookupHelper::findArgList(llvm::StringRef argList,
|
|
llvm::SmallVectorImpl<Expr*>& argExprs,
|
|
DiagSetting diagOnOff) const {
|
|
if (argList.empty()) return;
|
|
|
|
//
|
|
// Some utilities.
|
|
//
|
|
// Use P for shortness
|
|
Parser& P = *m_Parser;
|
|
StartParsingRAII ParseStarted(const_cast<LookupHelper&>(*this),
|
|
argList,
|
|
llvm::StringRef("arg.list.file"),
|
|
diagOnOff);
|
|
//
|
|
// Parse the arguments now.
|
|
//
|
|
{
|
|
bool hasUnusableResult = false;
|
|
while (P.getCurToken().isNot(tok::eof)) {
|
|
ExprResult Res = P.ParseAssignmentExpression();
|
|
if (Res.isUsable()) {
|
|
argExprs.push_back(Res.get());
|
|
}
|
|
else {
|
|
hasUnusableResult = true;
|
|
break;
|
|
}
|
|
if (!P.getCurToken().is(tok::comma)) {
|
|
break;
|
|
}
|
|
P.ConsumeToken();
|
|
}
|
|
if (hasUnusableResult)
|
|
// if one of the arguments is not usable return empty.
|
|
argExprs.clear();
|
|
}
|
|
}
|
|
|
|
static
|
|
bool hasFunctionSelector(DeclContext* ,
|
|
bool /* objectIsConst */,
|
|
const llvm::SmallVectorImpl<Expr*> &,
|
|
LookupResult &Result,
|
|
DeclarationNameInfo &,
|
|
const TemplateArgumentListInfo* ,
|
|
ASTContext&, Parser &, Sema &,
|
|
LookupHelper::DiagSetting /*diagOnOff*/) {
|
|
//
|
|
// Check for lookup failure.
|
|
//
|
|
if (Result.empty())
|
|
return false;
|
|
if (Result.isSingleResult())
|
|
return isa<FunctionDecl>(Result.getFoundDecl());
|
|
// We have many - those must be functions.
|
|
return true;
|
|
}
|
|
|
|
bool LookupHelper::hasFunction(const clang::Decl* scopeDecl,
|
|
llvm::StringRef funcName,
|
|
DiagSetting diagOnOff) const {
|
|
|
|
return execFindFunction<NoParse>(*m_Parser, m_Interpreter,
|
|
const_cast<LookupHelper&>(*this),
|
|
scopeDecl,
|
|
funcName, "",
|
|
false /* objectIsConst */,
|
|
hasFunctionSelector,
|
|
diagOnOff);
|
|
}
|
|
|
|
static const clang::Type* getType(LookupHelper* LH, llvm::StringRef Type) {
|
|
QualType Qt = LH->findType(Type, LookupHelper::WithDiagnostics);
|
|
assert(!Qt.isNull() && "Type should exist");
|
|
return Qt.getTypePtr();
|
|
}
|
|
|
|
LookupHelper::StringType
|
|
LookupHelper::getStringType(const clang::Type* Type) {
|
|
assert(Type && "Type cannot be null");
|
|
const Transaction*& Cache = m_Interpreter->getStdStringTransaction();
|
|
if (!Cache || !m_StringTy[kStdString]) {
|
|
// getStringType can be called multiple times with Cache being null, and
|
|
// the local cache should be discarded when that occurs.
|
|
if (!Cache)
|
|
m_StringTy = {{}};
|
|
QualType Qt = findType("std::string", WithDiagnostics);
|
|
m_StringTy[kStdString] = Qt.isNull() ? nullptr : Qt.getTypePtr();
|
|
if (!m_StringTy[kStdString]) return kNotAString;
|
|
|
|
Cache = m_Interpreter->getLatestTransaction();
|
|
m_StringTy[kWCharString] = getType(this, "std::wstring");
|
|
|
|
const clang::LangOptions& LO = m_Interpreter->getCI()->getLangOpts();
|
|
if (LO.CPlusPlus11) {
|
|
m_StringTy[kUTF16Str] = getType(this, "std::u16string");
|
|
m_StringTy[kUTF32Str] = getType(this, "std::u32string");
|
|
}
|
|
}
|
|
|
|
ASTContext& Ctx = m_Interpreter->getSema().getASTContext();
|
|
for (unsigned I = 0; I < kNumCachedStrings; ++I) {
|
|
if (m_StringTy[I] && Ctx.hasSameType(Type, m_StringTy[I]))
|
|
return StringType(I);
|
|
}
|
|
return kNotAString;
|
|
}
|
|
|
|
void LookupHelper::printStats() const {
|
|
llvm::errs() << "Cached entries: " << m_ParseBufferCache.size() << "\n";
|
|
llvm::errs() << "Total parse requests: " << m_TotalParseRequests << "\n";
|
|
llvm::errs() << "Cache hits: " << m_CacheHits << "\n";
|
|
}
|
|
} // end namespace cling
|