Merge pull request #31506 from hamishknight/hello-operator

This commit is contained in:
Hamish Knight
2020-05-23 09:48:21 -07:00
committed by GitHub
31 changed files with 904 additions and 480 deletions

View File

@@ -1050,72 +1050,6 @@ LookupConformanceInModuleRequest::evaluate(
return ProtocolConformanceRef(conformance);
}
namespace {
template <typename T>
struct OperatorLookup {
// Don't fold this into the static_assert: this would trigger an MSVC bug
// that causes the assertion to fail.
static constexpr T* ptr = static_cast<T*>(nullptr);
static_assert(ptr, "Only usable with operators");
};
template <>
struct OperatorLookup<PrefixOperatorDecl> {
static PrefixOperatorDecl *lookup(Evaluator &eval,
const OperatorLookupDescriptor &desc) {
// We can return the first prefix operator. All prefix operators of the
// same name are equivalent.
DirectOperatorLookupRequest req{desc, OperatorFixity::Prefix};
auto results = evaluateOrDefault(eval, req, {});
return results.empty() ? nullptr : cast<PrefixOperatorDecl>(results[0]);
}
};
template <>
struct OperatorLookup<InfixOperatorDecl> {
static InfixOperatorDecl *lookup(Evaluator &eval,
const OperatorLookupDescriptor &desc) {
// Return the first result if it exists.
DirectOperatorLookupRequest req{desc, OperatorFixity::Infix};
auto results = evaluateOrDefault(eval, req, {});
return results.empty() ? nullptr : cast<InfixOperatorDecl>(results[0]);
}
};
template <>
struct OperatorLookup<PostfixOperatorDecl> {
static PostfixOperatorDecl *lookup(Evaluator &eval,
const OperatorLookupDescriptor &desc) {
// We can return the first postfix operator. All postfix operators of the
// same name are equivalent.
DirectOperatorLookupRequest req{desc, OperatorFixity::Postfix};
auto results = evaluateOrDefault(eval, req, {});
return results.empty() ? nullptr : cast<PostfixOperatorDecl>(results[0]);
}
};
template <>
struct OperatorLookup<PrecedenceGroupDecl> {
static PrecedenceGroupDecl *lookup(Evaluator &eval,
const OperatorLookupDescriptor &desc) {
// Return the first result if it exists.
auto results =
evaluateOrDefault(eval, DirectPrecedenceGroupLookupRequest{desc}, {});
return results.empty() ? nullptr : results[0];
}
};
} // end anonymous namespace
/// A helper class to sneak around C++ access control rules.
class SourceFile::Impl {
public:
/// Only intended for use by lookupOperatorDeclForName.
static ArrayRef<SourceFile::ImportedModuleDesc>
getImportsForSourceFile(const SourceFile &SF) {
return *SF.Imports;
}
};
struct SourceFile::SourceFileSyntaxInfo {
const bool Enable;
/// The root of the syntax tree representing the source file.
@@ -1136,180 +1070,6 @@ void SourceFile::setSyntaxRoot(syntax::SourceFileSyntax &&Root) {
SyntaxInfo->SyntaxRoot.emplace(Root);
}
template<typename OP_DECL>
static Optional<OP_DECL *>
lookupOperatorDeclForName(ModuleDecl *M, SourceLoc Loc, Identifier Name,
bool isCascading);
template<typename OP_DECL>
using ImportedOperatorsMap = llvm::SmallDenseMap<OP_DECL*, bool, 16>;
template<typename OP_DECL>
static typename ImportedOperatorsMap<OP_DECL>::iterator
checkOperatorConflicts(const SourceFile &SF, SourceLoc loc,
ImportedOperatorsMap<OP_DECL> &importedOperators) {
// Check for conflicts.
auto i = importedOperators.begin(), end = importedOperators.end();
auto start = i;
for (++i; i != end; ++i) {
if (i->first->conflictsWith(start->first)) {
if (loc.isValid()) {
ASTContext &C = SF.getASTContext();
C.Diags.diagnose(loc, diag::ambiguous_operator_decls);
start->first->diagnose(diag::found_this_operator_decl);
i->first->diagnose(diag::found_this_operator_decl);
}
return end;
}
}
return start;
}
template<>
typename ImportedOperatorsMap<PrecedenceGroupDecl>::iterator
checkOperatorConflicts(const SourceFile &SF, SourceLoc loc,
ImportedOperatorsMap<PrecedenceGroupDecl> &importedGroups) {
if (importedGroups.size() == 1)
return importedGroups.begin();
// Any sort of ambiguity is an error.
if (loc.isValid()) {
ASTContext &C = SF.getASTContext();
C.Diags.diagnose(loc, diag::ambiguous_precedence_groups);
for (auto &entry : importedGroups) {
entry.first->diagnose(diag::found_this_precedence_group);
}
}
return importedGroups.end();
}
// Returns None on error, Optional(nullptr) if no operator decl found, or
// Optional(decl) if decl was found.
template <typename OP_DECL>
static Optional<OP_DECL *>
lookupOperatorDeclForName(const FileUnit &File, SourceLoc Loc,
Identifier Name, bool includePrivate,
bool isCascading) {
auto &eval = File.getASTContext().evaluator;
auto desc = OperatorLookupDescriptor::forFile(const_cast<FileUnit *>(&File),
Name, isCascading,
/*diagLoc*/ SourceLoc());
switch (File.getKind()) {
case FileUnitKind::Builtin:
// The Builtin module declares no operators.
return nullptr;
case FileUnitKind::Synthesized:
// Synthesized files currently declare no operators.
return nullptr;
case FileUnitKind::Source:
break;
case FileUnitKind::SerializedAST:
case FileUnitKind::ClangModule:
case FileUnitKind::DWARFModule:
return OperatorLookup<OP_DECL>::lookup(eval, desc);
}
auto &SF = cast<SourceFile>(File);
assert(SF.ASTStage >= SourceFile::ImportsResolved);
// Check if the decl exists on the file.
if (auto *op = OperatorLookup<OP_DECL>::lookup(eval, desc))
return op;
// Look for imported operator decls.
// Record whether they come from re-exported modules.
// FIXME: We ought to prefer operators elsewhere in this module before we
// check imports.
auto ownModule = SF.getParentModule();
ImportedOperatorsMap<OP_DECL> importedOperators;
for (auto &imported : SourceFile::Impl::getImportsForSourceFile(SF)) {
// Protect against source files that contrive to import their own modules.
if (imported.module.importedModule == ownModule)
continue;
bool isExported =
imported.importOptions.contains(SourceFile::ImportFlags::Exported);
if (!includePrivate && !isExported)
continue;
Optional<OP_DECL *> maybeOp = lookupOperatorDeclForName<OP_DECL>(
imported.module.importedModule, Loc, Name, isCascading);
if (!maybeOp)
return None;
if (OP_DECL *op = *maybeOp)
importedOperators[op] |= isExported;
}
llvm::PointerIntPair<OP_DECL *, 1, /*isPrivate*/ bool> result = {nullptr,
true};
if (!importedOperators.empty()) {
auto start = checkOperatorConflicts(SF, Loc, importedOperators);
if (start == importedOperators.end())
return None;
result = { start->first, start->second };
}
if (includePrivate || result.getInt())
return result.getPointer();
return nullptr;
}
template<typename OP_DECL>
static Optional<OP_DECL *>
lookupOperatorDeclForName(ModuleDecl *M, SourceLoc Loc, Identifier Name,
bool isCascading) {
OP_DECL *result = nullptr;
for (const FileUnit *File : M->getFiles()) {
auto next = lookupOperatorDeclForName<OP_DECL>(*File, Loc, Name, false,
isCascading);
if (!next.hasValue())
return next;
// FIXME: Diagnose ambiguity.
if (*next && result)
return None;
if (*next)
result = *next;
}
return result;
}
template <typename OperatorType>
OperatorType *LookupOperatorRequest<OperatorType>::evaluate(
Evaluator &evaluator, OperatorLookupDescriptor desc) const {
auto *file = desc.fileOrModule.get<FileUnit *>();
auto result =
lookupOperatorDeclForName<OperatorType>(*file, desc.diagLoc, desc.name,
/*includePrivate*/ true,
desc.isCascading);
if (!result.hasValue())
return nullptr;
if (!result.getValue()) {
result = lookupOperatorDeclForName<OperatorType>(file->getParentModule(),
desc.diagLoc, desc.name,
desc.isCascading);
}
return result.hasValue() ? result.getValue() : nullptr;
}
#define LOOKUP_OPERATOR(Kind) \
Kind##Decl *ModuleDecl::lookup##Kind(Identifier name, SourceLoc loc) { \
auto result = lookupOperatorDeclForName<Kind##Decl>( \
this, loc, name, /*isCascading*/ false); \
return result ? *result : nullptr; \
} \
template Kind##Decl *LookupOperatorRequest<Kind##Decl>::evaluate( \
Evaluator &e, OperatorLookupDescriptor) const;
LOOKUP_OPERATOR(PrefixOperator)
LOOKUP_OPERATOR(InfixOperator)
LOOKUP_OPERATOR(PostfixOperator)
LOOKUP_OPERATOR(PrecedenceGroup)
#undef LOOKUP_OPERATOR
void DirectOperatorLookupRequest::writeDependencySink(
evaluator::DependencyCollector &reqTracker,
TinyPtrVector<OperatorDecl *> ops) const {