mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Fix precedencegroup and operator decl lookup
Re-implement operator and precedencegroup decl lookup to use `namelookup::getAllImports` and existing decl shadowing logic. This allows us to find operator decls through `@_exported` imports, prefer operator decls defined in the same module over imported decls, and fixes a couple of other subtle issues. Because this new implementation is technically source breaking, as we can find multiple results where we used to only find one result, it's placed behind the new Frontend flag `-enable-new-operator-lookup` (with the aim of enabling it by default when we get a new language mode). However the new logic will always be used if the result is unambiguous. This means that e.g `@_exported` operators will be instantly available as long as there's only one candidate. If multiple candidates are found, we fall back to the old logic. Resolves SR-12132. Resolves rdar://59198796.
This commit is contained in:
@@ -7273,12 +7273,6 @@ public:
|
||||
|
||||
PrecedenceGroupDecl *getPrecedenceGroup() const;
|
||||
|
||||
/// True if this decl's attributes conflict with those declared by another
|
||||
/// operator.
|
||||
bool conflictsWith(InfixOperatorDecl *other) {
|
||||
return getPrecedenceGroup() != other->getPrecedenceGroup();
|
||||
}
|
||||
|
||||
static bool classof(const Decl *D) {
|
||||
return D->getKind() == DeclKind::InfixOperator;
|
||||
}
|
||||
@@ -7307,12 +7301,6 @@ public:
|
||||
return { getOperatorLoc(), getNameLoc() };
|
||||
}
|
||||
|
||||
/// True if this decl's attributes conflict with those declared by another
|
||||
/// PrefixOperatorDecl.
|
||||
bool conflictsWith(PrefixOperatorDecl *other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool classof(const Decl *D) {
|
||||
return D->getKind() == DeclKind::PrefixOperator;
|
||||
}
|
||||
@@ -7341,12 +7329,6 @@ public:
|
||||
return { getOperatorLoc(), getNameLoc() };
|
||||
}
|
||||
|
||||
/// True if this decl's attributes conflict with those declared by another
|
||||
/// PostfixOperatorDecl.
|
||||
bool conflictsWith(PostfixOperatorDecl *other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool classof(const Decl *D) {
|
||||
return D->getKind() == DeclKind::PostfixOperator;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ namespace swift {
|
||||
class GenericSignature;
|
||||
class GenericTypeParamDecl;
|
||||
class GenericTypeParamType;
|
||||
class InfixOperatorDecl;
|
||||
class InfixOperatorLookupResult;
|
||||
class PrecedenceGroupDecl;
|
||||
class ProtocolDecl;
|
||||
class Requirement;
|
||||
class SourceFile;
|
||||
@@ -61,6 +64,9 @@ namespace swift {
|
||||
class ModuleDecl;
|
||||
class GenericTypeDecl;
|
||||
class NominalTypeDecl;
|
||||
class PrecedenceGroupLookupResult;
|
||||
class PostfixOperatorDecl;
|
||||
class PrefixOperatorDecl;
|
||||
class ProtocolConformance;
|
||||
class ValueDecl;
|
||||
class Initializer;
|
||||
@@ -561,6 +567,29 @@ public:
|
||||
ObjCSelector selector,
|
||||
SmallVectorImpl<AbstractFunctionDecl *> &results) const;
|
||||
|
||||
/// Looks up an infix operator with a given \p name.
|
||||
///
|
||||
/// This returns a vector of results, as it's possible to find multiple infix
|
||||
/// operators with different precedence groups.
|
||||
InfixOperatorLookupResult lookupInfixOperator(Identifier name) const;
|
||||
|
||||
/// Looks up an prefix operator with a given \p name.
|
||||
///
|
||||
/// If multiple results are found, one is chosen in a stable manner, as
|
||||
/// prefix operator decls cannot differ other than in name. If no results are
|
||||
/// found, returns \c nullptr.
|
||||
PrefixOperatorDecl *lookupPrefixOperator(Identifier name) const;
|
||||
|
||||
/// Looks up an postfix operator with a given \p name.
|
||||
///
|
||||
/// If multiple results are found, one is chosen in a stable manner, as
|
||||
/// postfix operator decls cannot differ other than in name. If no results are
|
||||
/// found, returns \c nullptr.
|
||||
PostfixOperatorDecl *lookupPostfixOperator(Identifier name) const;
|
||||
|
||||
/// Looks up a precedence group with a given \p name.
|
||||
PrecedenceGroupLookupResult lookupPrecedenceGroup(Identifier name) const;
|
||||
|
||||
/// Return the ASTContext for a specified DeclContext by
|
||||
/// walking up to the enclosing module and returning its ASTContext.
|
||||
LLVM_READONLY
|
||||
|
||||
@@ -592,21 +592,6 @@ public:
|
||||
/// FIXME: Remove the integrated REPL.
|
||||
void clearLookupCache();
|
||||
|
||||
/// @{
|
||||
|
||||
/// Look up the given operator in this module.
|
||||
///
|
||||
/// If the operator is not found, or if there is an ambiguity, returns null.
|
||||
InfixOperatorDecl *lookupInfixOperator(Identifier name,
|
||||
SourceLoc diagLoc = {});
|
||||
PrefixOperatorDecl *lookupPrefixOperator(Identifier name,
|
||||
SourceLoc diagLoc = {});
|
||||
PostfixOperatorDecl *lookupPostfixOperator(Identifier name,
|
||||
SourceLoc diagLoc = {});
|
||||
PrecedenceGroupDecl *lookupPrecedenceGroup(Identifier name,
|
||||
SourceLoc diagLoc = {});
|
||||
/// @}
|
||||
|
||||
/// Finds all class members defined in this module.
|
||||
///
|
||||
/// This does a simple local lookup, not recursively looking through imports.
|
||||
|
||||
@@ -594,14 +594,10 @@ public:
|
||||
using Storage = llvm::PointerUnion<FileUnit *, ModuleDecl *>;
|
||||
Storage fileOrModule;
|
||||
Identifier name;
|
||||
bool isCascading;
|
||||
SourceLoc diagLoc;
|
||||
|
||||
private:
|
||||
OperatorLookupDescriptor(Storage fileOrModule, Identifier name,
|
||||
bool isCascading, SourceLoc diagLoc)
|
||||
: fileOrModule(fileOrModule), name(name), isCascading(isCascading),
|
||||
diagLoc(diagLoc) {}
|
||||
OperatorLookupDescriptor(Storage fileOrModule, Identifier name)
|
||||
: fileOrModule(fileOrModule), name(name) {}
|
||||
|
||||
public:
|
||||
/// Retrieves the files to perform lookup in.
|
||||
@@ -613,14 +609,20 @@ public:
|
||||
return fileOrModule.dyn_cast<ModuleDecl *>();
|
||||
}
|
||||
|
||||
/// Retrieve the file or module for the lookup, as a DeclContext.
|
||||
DeclContext *getDC() const {
|
||||
if (auto *module = getModule())
|
||||
return module;
|
||||
return fileOrModule.get<FileUnit *>();
|
||||
}
|
||||
|
||||
friend llvm::hash_code hash_value(const OperatorLookupDescriptor &desc) {
|
||||
return llvm::hash_combine(desc.fileOrModule, desc.name, desc.isCascading);
|
||||
return llvm::hash_combine(desc.fileOrModule, desc.name);
|
||||
}
|
||||
|
||||
friend bool operator==(const OperatorLookupDescriptor &lhs,
|
||||
const OperatorLookupDescriptor &rhs) {
|
||||
return lhs.fileOrModule == rhs.fileOrModule && lhs.name == rhs.name &&
|
||||
lhs.isCascading == rhs.isCascading;
|
||||
return lhs.fileOrModule == rhs.fileOrModule && lhs.name == rhs.name;
|
||||
}
|
||||
|
||||
friend bool operator!=(const OperatorLookupDescriptor &lhs,
|
||||
@@ -628,16 +630,15 @@ public:
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
static OperatorLookupDescriptor forFile(FileUnit *file, Identifier name,
|
||||
bool isCascading, SourceLoc diagLoc) {
|
||||
return OperatorLookupDescriptor(file, name, isCascading, diagLoc);
|
||||
static OperatorLookupDescriptor forFile(FileUnit *file, Identifier name) {
|
||||
return OperatorLookupDescriptor(file, name);
|
||||
}
|
||||
|
||||
static OperatorLookupDescriptor forModule(ModuleDecl *mod, Identifier name,
|
||||
bool isCascading,
|
||||
SourceLoc diagLoc) {
|
||||
return OperatorLookupDescriptor(mod, name, isCascading, diagLoc);
|
||||
static OperatorLookupDescriptor forModule(ModuleDecl *mod, Identifier name) {
|
||||
return OperatorLookupDescriptor(mod, name);
|
||||
}
|
||||
|
||||
static OperatorLookupDescriptor forDC(const DeclContext *DC, Identifier name);
|
||||
};
|
||||
|
||||
void simple_display(llvm::raw_ostream &out,
|
||||
@@ -645,34 +646,6 @@ void simple_display(llvm::raw_ostream &out,
|
||||
|
||||
SourceLoc extractNearestSourceLoc(const OperatorLookupDescriptor &desc);
|
||||
|
||||
template <typename OperatorType>
|
||||
class LookupOperatorRequest
|
||||
: public SimpleRequest<LookupOperatorRequest<OperatorType>,
|
||||
OperatorType *(OperatorLookupDescriptor),
|
||||
RequestFlags::Cached> {
|
||||
using SimpleRequest<LookupOperatorRequest<OperatorType>,
|
||||
OperatorType *(OperatorLookupDescriptor),
|
||||
RequestFlags::Cached>::SimpleRequest;
|
||||
|
||||
private:
|
||||
friend SimpleRequest<LookupOperatorRequest<OperatorType>,
|
||||
OperatorType *(OperatorLookupDescriptor),
|
||||
RequestFlags::Cached>;
|
||||
|
||||
// Evaluation.
|
||||
OperatorType *evaluate(Evaluator &evaluator,
|
||||
OperatorLookupDescriptor desc) const;
|
||||
|
||||
public:
|
||||
// Cached.
|
||||
bool isCached() const { return true; }
|
||||
};
|
||||
|
||||
using LookupPrefixOperatorRequest = LookupOperatorRequest<PrefixOperatorDecl>;
|
||||
using LookupInfixOperatorRequest = LookupOperatorRequest<InfixOperatorDecl>;
|
||||
using LookupPostfixOperatorRequest = LookupOperatorRequest<PostfixOperatorDecl>;
|
||||
using LookupPrecedenceGroupRequest = LookupOperatorRequest<PrecedenceGroupDecl>;
|
||||
|
||||
/// Looks up an operator in a given file or module without looking through
|
||||
/// imports.
|
||||
class DirectOperatorLookupRequest
|
||||
@@ -770,6 +743,84 @@ public:
|
||||
ProtocolConformanceRef result) const;
|
||||
};
|
||||
|
||||
/// Look up an 'infix operator' decl by name.
|
||||
class LookupInfixOperatorRequest
|
||||
: public SimpleRequest<LookupInfixOperatorRequest,
|
||||
TinyPtrVector<InfixOperatorDecl *>(
|
||||
OperatorLookupDescriptor),
|
||||
RequestFlags::Cached> {
|
||||
public:
|
||||
using SimpleRequest::SimpleRequest;
|
||||
|
||||
private:
|
||||
friend SimpleRequest;
|
||||
|
||||
TinyPtrVector<InfixOperatorDecl *>
|
||||
evaluate(Evaluator &evaluator, OperatorLookupDescriptor desc) const;
|
||||
|
||||
public:
|
||||
// Cached.
|
||||
bool isCached() const { return true; }
|
||||
};
|
||||
|
||||
/// Look up an 'prefix operator' decl by name.
|
||||
class LookupPrefixOperatorRequest
|
||||
: public SimpleRequest<LookupPrefixOperatorRequest,
|
||||
PrefixOperatorDecl *(OperatorLookupDescriptor),
|
||||
RequestFlags::Cached> {
|
||||
public:
|
||||
using SimpleRequest::SimpleRequest;
|
||||
|
||||
private:
|
||||
friend SimpleRequest;
|
||||
|
||||
PrefixOperatorDecl *evaluate(Evaluator &evaluator,
|
||||
OperatorLookupDescriptor desc) const;
|
||||
|
||||
public:
|
||||
// Cached.
|
||||
bool isCached() const { return true; }
|
||||
};
|
||||
|
||||
/// Look up an 'postfix operator' decl by name.
|
||||
class LookupPostfixOperatorRequest
|
||||
: public SimpleRequest<LookupPostfixOperatorRequest,
|
||||
PostfixOperatorDecl *(OperatorLookupDescriptor),
|
||||
RequestFlags::Cached> {
|
||||
public:
|
||||
using SimpleRequest::SimpleRequest;
|
||||
|
||||
private:
|
||||
friend SimpleRequest;
|
||||
|
||||
PostfixOperatorDecl *evaluate(Evaluator &evaluator,
|
||||
OperatorLookupDescriptor desc) const;
|
||||
|
||||
public:
|
||||
// Cached.
|
||||
bool isCached() const { return true; }
|
||||
};
|
||||
|
||||
/// Look up a precedencegroup decl by name.
|
||||
class LookupPrecedenceGroupRequest
|
||||
: public SimpleRequest<LookupPrecedenceGroupRequest,
|
||||
TinyPtrVector<PrecedenceGroupDecl *>(
|
||||
OperatorLookupDescriptor),
|
||||
RequestFlags::Cached> {
|
||||
public:
|
||||
using SimpleRequest::SimpleRequest;
|
||||
|
||||
private:
|
||||
friend SimpleRequest;
|
||||
|
||||
TinyPtrVector<PrecedenceGroupDecl *>
|
||||
evaluate(Evaluator &evaluator, OperatorLookupDescriptor descriptor) const;
|
||||
|
||||
public:
|
||||
// Cached.
|
||||
bool isCached() const { return true; }
|
||||
};
|
||||
|
||||
#define SWIFT_TYPEID_ZONE NameLookup
|
||||
#define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def"
|
||||
#include "swift/Basic/DefineTypeIDZone.h"
|
||||
|
||||
@@ -89,11 +89,11 @@ SWIFT_REQUEST(NameLookup, LookupPrefixOperatorRequest,
|
||||
PrefixOperatorDecl *(OperatorLookupDescriptor),
|
||||
Cached, NoLocationInfo)
|
||||
SWIFT_REQUEST(NameLookup, LookupInfixOperatorRequest,
|
||||
InfixOperatorDecl *(OperatorLookupDescriptor),
|
||||
TinyPtrVector<InfixOperatorDecl *>(OperatorLookupDescriptor),
|
||||
Cached, NoLocationInfo)
|
||||
SWIFT_REQUEST(NameLookup, LookupPostfixOperatorRequest,
|
||||
PostfixOperatorDecl *(OperatorLookupDescriptor),
|
||||
Cached, NoLocationInfo)
|
||||
SWIFT_REQUEST(NameLookup, LookupPrecedenceGroupRequest,
|
||||
PrecedenceGroupDecl *(OperatorLookupDescriptor),
|
||||
TinyPtrVector<PrecedenceGroupDecl *>(OperatorLookupDescriptor),
|
||||
Cached, NoLocationInfo)
|
||||
|
||||
127
include/swift/AST/OperatorNameLookup.h
Normal file
127
include/swift/AST/OperatorNameLookup.h
Normal file
@@ -0,0 +1,127 @@
|
||||
//===--- OperatorNameLookup.h - Operator and Precedence Lookup --*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2020 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines interfaces for looking up operator and precedence group
|
||||
// declarations.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SWIFT_AST_OPERATOR_NAME_LOOKUP_H
|
||||
#define SWIFT_AST_OPERATOR_NAME_LOOKUP_H
|
||||
|
||||
#include "swift/AST/Identifier.h"
|
||||
#include "swift/AST/Module.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
|
||||
namespace swift {
|
||||
|
||||
/// Base class for infix operator and precedence group lookup results.
|
||||
template <typename Derived, typename DeclTy>
|
||||
class OperatorLookupResultBase {
|
||||
protected:
|
||||
const DeclContext *ModuleDC;
|
||||
Identifier Name;
|
||||
TinyPtrVector<DeclTy *> Results;
|
||||
|
||||
private:
|
||||
const Derived &asDerived() const {
|
||||
return *static_cast<const Derived *>(this);
|
||||
}
|
||||
|
||||
public:
|
||||
OperatorLookupResultBase(const DeclContext *moduleDC, Identifier name,
|
||||
TinyPtrVector<DeclTy *> &&results)
|
||||
: ModuleDC(moduleDC), Name(name), Results(std::move(results)) {}
|
||||
|
||||
/// Retrieve the underlying results vector.
|
||||
TinyPtrVector<DeclTy *> get() && { return std::move(Results); }
|
||||
|
||||
/// If the lookup produced a single result, returns it. Otherwise returns
|
||||
/// \c nullptr.
|
||||
DeclTy *getSingle() const {
|
||||
return Results.size() == 1 ? Results[0] : nullptr;
|
||||
}
|
||||
|
||||
/// If the lookup produced a single result, returns it. Otherwise, emits an
|
||||
/// ambiguity or missing decl diagnostic, and returns \c nullptr.
|
||||
DeclTy *getSingleOrDiagnose(SourceLoc loc, bool forBuiltin = false) const {
|
||||
if (auto single = getSingle())
|
||||
return single;
|
||||
|
||||
if (isAmbiguous()) {
|
||||
asDerived().diagnoseAmbiguity(loc);
|
||||
} else {
|
||||
assert(!hasResults());
|
||||
asDerived().diagnoseMissing(loc, forBuiltin);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
using const_iterator = typename decltype(Results)::const_iterator;
|
||||
const_iterator begin() const { return Results.begin(); }
|
||||
const_iterator end() const { return Results.end(); }
|
||||
|
||||
/// Whether the lookup produced at least one result.
|
||||
bool hasResults() const { return !Results.empty(); }
|
||||
|
||||
/// Whether the lookup produced multiple ambiguous results.
|
||||
bool isAmbiguous() const { return Results.size() > 1; }
|
||||
|
||||
friend bool operator==(const OperatorLookupResultBase &lhs,
|
||||
const OperatorLookupResultBase &rhs) {
|
||||
return lhs.Results == rhs.Results;
|
||||
}
|
||||
friend bool operator!=(const OperatorLookupResultBase &lhs,
|
||||
const OperatorLookupResultBase &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
friend void simple_display(llvm::raw_ostream &out,
|
||||
const OperatorLookupResultBase &result) {
|
||||
simple_display(out, result.Results);
|
||||
}
|
||||
};
|
||||
|
||||
/// The result of a precedence group lookup.
|
||||
class PrecedenceGroupLookupResult final
|
||||
: public OperatorLookupResultBase<PrecedenceGroupLookupResult,
|
||||
PrecedenceGroupDecl> {
|
||||
friend class OperatorLookupResultBase<PrecedenceGroupLookupResult,
|
||||
PrecedenceGroupDecl>;
|
||||
|
||||
public:
|
||||
PrecedenceGroupLookupResult(const DeclContext *moduleDC, Identifier name,
|
||||
TinyPtrVector<PrecedenceGroupDecl *> &&results)
|
||||
: OperatorLookupResultBase(moduleDC, name, std::move(results)) {}
|
||||
|
||||
void diagnoseAmbiguity(SourceLoc loc) const;
|
||||
void diagnoseMissing(SourceLoc loc, bool forBuiltin) const;
|
||||
};
|
||||
|
||||
/// The result of an infix operator lookup.
|
||||
class InfixOperatorLookupResult final
|
||||
: public OperatorLookupResultBase<InfixOperatorLookupResult,
|
||||
InfixOperatorDecl> {
|
||||
friend class OperatorLookupResultBase<InfixOperatorLookupResult,
|
||||
InfixOperatorDecl>;
|
||||
|
||||
public:
|
||||
InfixOperatorLookupResult(const DeclContext *moduleDC, Identifier name,
|
||||
TinyPtrVector<InfixOperatorDecl *> &&results)
|
||||
: OperatorLookupResultBase(moduleDC, name, std::move(results)) {}
|
||||
|
||||
void diagnoseAmbiguity(SourceLoc loc) const;
|
||||
void diagnoseMissing(SourceLoc loc, bool forBuiltin) const;
|
||||
};
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
#endif
|
||||
@@ -30,7 +30,6 @@ class SourceFile final : public FileUnit {
|
||||
friend class ParseSourceFileRequest;
|
||||
|
||||
public:
|
||||
class Impl;
|
||||
struct SourceFileSyntaxInfo;
|
||||
|
||||
/// Possible attributes for imports in source files.
|
||||
@@ -198,7 +197,6 @@ private:
|
||||
ParserStatePtr(/*ptr*/ nullptr, /*deleter*/ nullptr);
|
||||
|
||||
friend ASTContext;
|
||||
friend Impl;
|
||||
|
||||
public:
|
||||
/// Appends the given declaration to the end of the top-level decls list. Do
|
||||
|
||||
@@ -1531,7 +1531,8 @@ void simple_display(llvm::raw_ostream &out, const PrecedenceGroupDescriptor &d);
|
||||
|
||||
class ValidatePrecedenceGroupRequest
|
||||
: public SimpleRequest<ValidatePrecedenceGroupRequest,
|
||||
PrecedenceGroupDecl *(PrecedenceGroupDescriptor),
|
||||
TinyPtrVector<PrecedenceGroupDecl *>(
|
||||
PrecedenceGroupDescriptor),
|
||||
RequestFlags::Cached> {
|
||||
public:
|
||||
using SimpleRequest::SimpleRequest;
|
||||
@@ -1540,7 +1541,7 @@ private:
|
||||
friend SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
PrecedenceGroupDecl *
|
||||
TinyPtrVector<PrecedenceGroupDecl *>
|
||||
evaluate(Evaluator &evaluator, PrecedenceGroupDescriptor descriptor) const;
|
||||
|
||||
public:
|
||||
|
||||
@@ -61,6 +61,7 @@ add_swift_host_library(swiftAST STATIC
|
||||
ModuleNameLookup.cpp
|
||||
NameLookup.cpp
|
||||
NameLookupRequests.cpp
|
||||
OperatorNameLookup.cpp
|
||||
Parameter.cpp
|
||||
Pattern.cpp
|
||||
PlatformKind.cpp
|
||||
|
||||
@@ -1051,72 +1051,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.
|
||||
@@ -1137,180 +1071,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 {
|
||||
|
||||
@@ -281,6 +281,17 @@ SourceLoc swift::extractNearestSourceLoc(const DirectLookupDescriptor &desc) {
|
||||
// LookupOperatorRequest computation.
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
OperatorLookupDescriptor OperatorLookupDescriptor::forDC(const DeclContext *DC,
|
||||
Identifier name) {
|
||||
auto *moduleDC = DC->getModuleScopeContext();
|
||||
if (auto *file = dyn_cast<FileUnit>(moduleDC)) {
|
||||
return OperatorLookupDescriptor::forFile(file, name);
|
||||
} else {
|
||||
auto *mod = cast<ModuleDecl>(moduleDC->getAsDecl());
|
||||
return OperatorLookupDescriptor::forModule(mod, name);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayRef<FileUnit *> OperatorLookupDescriptor::getFiles() const {
|
||||
if (auto *module = getModule())
|
||||
return module->getFiles();
|
||||
@@ -298,7 +309,7 @@ void swift::simple_display(llvm::raw_ostream &out,
|
||||
}
|
||||
|
||||
SourceLoc swift::extractNearestSourceLoc(const OperatorLookupDescriptor &desc) {
|
||||
return desc.diagLoc;
|
||||
return extractNearestSourceLoc(desc.fileOrModule);
|
||||
}
|
||||
|
||||
void DirectLookupRequest::writeDependencySink(
|
||||
|
||||
243
lib/AST/OperatorNameLookup.cpp
Normal file
243
lib/AST/OperatorNameLookup.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
//===--- OperatorNameLookup.cpp - Operator and Precedence Lookup *- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2020 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements interfaces for performing operator and precedence group
|
||||
// declaration lookup.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "swift/AST/OperatorNameLookup.h"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/DiagnosticsSema.h"
|
||||
#include "swift/AST/ImportCache.h"
|
||||
#include "swift/AST/NameLookupRequests.h"
|
||||
|
||||
#define DEBUG_TYPE "operator-name-lookup"
|
||||
|
||||
using namespace swift;
|
||||
using namespace swift::namelookup;
|
||||
|
||||
template <typename T>
|
||||
static TinyPtrVector<T *> lookupOperatorImpl(
|
||||
DeclContext *moduleDC, Identifier name,
|
||||
llvm::function_ref<void(OperatorLookupDescriptor, TinyPtrVector<T *> &)>
|
||||
lookupDirect) {
|
||||
assert(moduleDC->isModuleScopeContext());
|
||||
auto &ctx = moduleDC->getASTContext();
|
||||
|
||||
// First try to use the new operator lookup logic.
|
||||
{
|
||||
TinyPtrVector<T *> results;
|
||||
for (auto &import : getAllImports(moduleDC)) {
|
||||
auto *mod = import.importedModule;
|
||||
lookupDirect(OperatorLookupDescriptor::forModule(mod, name), results);
|
||||
}
|
||||
|
||||
// If there aren't multiple results, or the new logic is completely enabled,
|
||||
// perform shadowing checks and return. Otherwise fall through to the old
|
||||
// logic.
|
||||
if (results.size() <= 1 || ctx.LangOpts.EnableNewOperatorLookup) {
|
||||
removeShadowedDecls(results, moduleDC);
|
||||
return std::move(results);
|
||||
}
|
||||
}
|
||||
|
||||
// There are three stages to the old operator lookup:
|
||||
// 1) Lookup directly in the file.
|
||||
// 2) Lookup in the file's direct imports (not looking through exports).
|
||||
// 3) Lookup in other files.
|
||||
// If any step yields results, we return them without performing the next
|
||||
// steps. Note that this means when we come to look in other files, we can
|
||||
// accumulate ambiguities across files, unlike when looking in the original
|
||||
// file, where we can bail early.
|
||||
|
||||
TinyPtrVector<T *> results;
|
||||
SmallPtrSet<ModuleDecl *, 8> visitedModules;
|
||||
|
||||
// Protect against source files that contrive to import their own modules.
|
||||
visitedModules.insert(moduleDC->getParentModule());
|
||||
|
||||
auto lookupInFileAndImports = [&](FileUnit *file,
|
||||
bool includePrivate) -> bool {
|
||||
// If we find something in the file itself, bail without checking imports.
|
||||
lookupDirect(OperatorLookupDescriptor::forFile(file, name), results);
|
||||
if (!results.empty())
|
||||
return true;
|
||||
|
||||
// Only look into SourceFile imports.
|
||||
auto *SF = dyn_cast<SourceFile>(file);
|
||||
if (!SF)
|
||||
return false;
|
||||
|
||||
for (auto &import : SF->getImports()) {
|
||||
auto *mod = import.module.importedModule;
|
||||
if (!visitedModules.insert(mod).second)
|
||||
continue;
|
||||
|
||||
bool isExported =
|
||||
import.importOptions.contains(SourceFile::ImportFlags::Exported);
|
||||
if (!includePrivate && !isExported)
|
||||
continue;
|
||||
|
||||
lookupDirect(OperatorLookupDescriptor::forModule(mod, name), results);
|
||||
}
|
||||
return !results.empty();
|
||||
};
|
||||
|
||||
// If we have a SourceFile context, search it and its private imports.
|
||||
auto *SF = dyn_cast<SourceFile>(moduleDC);
|
||||
if (SF && lookupInFileAndImports(SF, /*includePrivate*/ true))
|
||||
return std::move(results);
|
||||
|
||||
// Search all the other files of the module, this time excluding private
|
||||
// imports.
|
||||
auto *mod = moduleDC->getParentModule();
|
||||
for (auto *file : mod->getFiles()) {
|
||||
if (file != SF)
|
||||
lookupInFileAndImports(file, /*includePrivate*/ false);
|
||||
}
|
||||
return std::move(results);
|
||||
}
|
||||
|
||||
static TinyPtrVector<OperatorDecl *>
|
||||
lookupOperator(DeclContext *moduleDC, Identifier name, OperatorFixity fixity) {
|
||||
auto &eval = moduleDC->getASTContext().evaluator;
|
||||
return lookupOperatorImpl<OperatorDecl>(
|
||||
moduleDC, name,
|
||||
[&](OperatorLookupDescriptor desc,
|
||||
TinyPtrVector<OperatorDecl *> &results) {
|
||||
auto ops = evaluateOrDefault(
|
||||
eval, DirectOperatorLookupRequest{desc, fixity}, {});
|
||||
for (auto *op : ops)
|
||||
results.push_back(op);
|
||||
});
|
||||
}
|
||||
|
||||
void InfixOperatorLookupResult::diagnoseAmbiguity(SourceLoc loc) const {
|
||||
auto &ctx = ModuleDC->getASTContext();
|
||||
ctx.Diags.diagnose(loc, diag::ambiguous_operator_decls);
|
||||
for (auto *op : *this)
|
||||
op->diagnose(diag::found_this_operator_decl);
|
||||
}
|
||||
|
||||
void InfixOperatorLookupResult::diagnoseMissing(SourceLoc loc,
|
||||
bool forBuiltin) const {
|
||||
ModuleDC->getASTContext().Diags.diagnose(loc, diag::unknown_binop);
|
||||
}
|
||||
|
||||
TinyPtrVector<InfixOperatorDecl *>
|
||||
LookupInfixOperatorRequest::evaluate(Evaluator &evaluator,
|
||||
OperatorLookupDescriptor desc) const {
|
||||
auto ops = lookupOperator(desc.getDC(), desc.name, OperatorFixity::Infix);
|
||||
|
||||
// If we have a single result, return it directly. This avoids having to look
|
||||
// up its precedence group.
|
||||
if (ops.size() == 1)
|
||||
return {cast<InfixOperatorDecl>(ops[0])};
|
||||
|
||||
// Otherwise take the first infix operator we see with a particular precedence
|
||||
// group. This avoids an ambiguity if two different modules declare the same
|
||||
// operator with the same precedence.
|
||||
TinyPtrVector<InfixOperatorDecl *> results;
|
||||
SmallPtrSet<PrecedenceGroupDecl *, 2> groups;
|
||||
for (auto *op : ops) {
|
||||
auto *infix = cast<InfixOperatorDecl>(op);
|
||||
if (groups.insert(infix->getPrecedenceGroup()).second)
|
||||
results.push_back(infix);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
InfixOperatorLookupResult
|
||||
DeclContext::lookupInfixOperator(Identifier name) const {
|
||||
auto desc = OperatorLookupDescriptor::forDC(this, name);
|
||||
auto ops = evaluateOrDefault(getASTContext().evaluator,
|
||||
LookupInfixOperatorRequest{desc}, {});
|
||||
// Wrap the result in a InfixOperatorLookupResult. The request doesn't
|
||||
// return this directly to avoid unnecessarily caching the name and context.
|
||||
return InfixOperatorLookupResult(this, name, std::move(ops));
|
||||
}
|
||||
|
||||
PrefixOperatorDecl *
|
||||
LookupPrefixOperatorRequest::evaluate(Evaluator &evaluator,
|
||||
OperatorLookupDescriptor desc) const {
|
||||
auto ops = lookupOperator(desc.getDC(), desc.name, OperatorFixity::Prefix);
|
||||
if (ops.empty())
|
||||
return nullptr;
|
||||
|
||||
// We can return the first prefix operator. All prefix operators of the same
|
||||
// name are equivalent.
|
||||
return cast<PrefixOperatorDecl>(ops[0]);
|
||||
}
|
||||
|
||||
PrefixOperatorDecl *DeclContext::lookupPrefixOperator(Identifier name) const {
|
||||
auto desc = OperatorLookupDescriptor::forDC(this, name);
|
||||
return evaluateOrDefault(getASTContext().evaluator,
|
||||
LookupPrefixOperatorRequest{desc}, nullptr);
|
||||
}
|
||||
|
||||
PostfixOperatorDecl *
|
||||
LookupPostfixOperatorRequest::evaluate(Evaluator &evaluator,
|
||||
OperatorLookupDescriptor desc) const {
|
||||
auto ops = lookupOperator(desc.getDC(), desc.name, OperatorFixity::Postfix);
|
||||
if (ops.empty())
|
||||
return nullptr;
|
||||
|
||||
// We can return the first postfix operator. All postfix operators of the same
|
||||
// name are equivalent.
|
||||
return cast<PostfixOperatorDecl>(ops[0]);
|
||||
}
|
||||
|
||||
PostfixOperatorDecl *DeclContext::lookupPostfixOperator(Identifier name) const {
|
||||
auto desc = OperatorLookupDescriptor::forDC(this, name);
|
||||
return evaluateOrDefault(getASTContext().evaluator,
|
||||
LookupPostfixOperatorRequest{desc}, nullptr);
|
||||
}
|
||||
|
||||
void PrecedenceGroupLookupResult::diagnoseAmbiguity(SourceLoc loc) const {
|
||||
auto &ctx = ModuleDC->getASTContext();
|
||||
ctx.Diags.diagnose(loc, diag::ambiguous_precedence_groups);
|
||||
for (auto *group : *this)
|
||||
group->diagnose(diag::found_this_precedence_group);
|
||||
}
|
||||
|
||||
void PrecedenceGroupLookupResult::diagnoseMissing(SourceLoc loc,
|
||||
bool forBuiltin) const {
|
||||
auto &ctx = ModuleDC->getASTContext();
|
||||
auto diagID = forBuiltin ? diag::missing_builtin_precedence_group
|
||||
: diag::unknown_precedence_group;
|
||||
ctx.Diags.diagnose(loc, diagID, Name);
|
||||
}
|
||||
|
||||
TinyPtrVector<PrecedenceGroupDecl *>
|
||||
LookupPrecedenceGroupRequest::evaluate(Evaluator &evaluator,
|
||||
OperatorLookupDescriptor desc) const {
|
||||
return lookupOperatorImpl<PrecedenceGroupDecl>(
|
||||
desc.getDC(), desc.name,
|
||||
[&](OperatorLookupDescriptor desc,
|
||||
TinyPtrVector<PrecedenceGroupDecl *> &results) {
|
||||
auto groups = evaluateOrDefault(
|
||||
evaluator, DirectPrecedenceGroupLookupRequest{desc}, {});
|
||||
for (auto *group : groups)
|
||||
results.push_back(group);
|
||||
});
|
||||
}
|
||||
|
||||
PrecedenceGroupLookupResult
|
||||
DeclContext::lookupPrecedenceGroup(Identifier name) const {
|
||||
auto desc = OperatorLookupDescriptor::forDC(this, name);
|
||||
auto groups = evaluateOrDefault(getASTContext().evaluator,
|
||||
LookupPrecedenceGroupRequest{desc}, {});
|
||||
// Wrap the result in a PrecedenceGroupLookupResult. The request doesn't
|
||||
// return this directly to avoid unnecessarily caching the name and context.
|
||||
return PrecedenceGroupLookupResult(this, name, std::move(groups));
|
||||
}
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "swift/AST/Initializer.h"
|
||||
#include "swift/AST/GenericEnvironment.h"
|
||||
#include "swift/AST/GenericSignature.h"
|
||||
#include "swift/AST/OperatorNameLookup.h"
|
||||
#include "swift/AST/ParameterList.h"
|
||||
#include "swift/AST/ProtocolConformance.h"
|
||||
#include "swift/AST/SubstitutionMap.h"
|
||||
@@ -7637,8 +7638,10 @@ bool swift::exprNeedsParensOutsideFollowingOperator(
|
||||
|
||||
bool swift::exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC,
|
||||
Expr *expr) {
|
||||
auto &ctx = DC->getASTContext();
|
||||
auto asPG = TypeChecker::lookupPrecedenceGroup(
|
||||
DC, DC->getASTContext().Id_NilCoalescingPrecedence, SourceLoc());
|
||||
DC, ctx.Id_NilCoalescingPrecedence, SourceLoc())
|
||||
.getSingle();
|
||||
if (!asPG)
|
||||
return true;
|
||||
return exprNeedsParensInsideFollowingOperator(DC, expr, asPG);
|
||||
@@ -7647,9 +7650,12 @@ bool swift::exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC,
|
||||
bool swift::exprNeedsParensAfterAddingNilCoalescing(DeclContext *DC,
|
||||
Expr *expr,
|
||||
Expr *rootExpr) {
|
||||
auto &ctx = DC->getASTContext();
|
||||
auto asPG = TypeChecker::lookupPrecedenceGroup(
|
||||
DC, DC->getASTContext().Id_NilCoalescingPrecedence, SourceLoc());
|
||||
if (!asPG) return true;
|
||||
DC, ctx.Id_NilCoalescingPrecedence, SourceLoc())
|
||||
.getSingle();
|
||||
if (!asPG)
|
||||
return true;
|
||||
return exprNeedsParensOutsideFollowingOperator(DC, expr, rootExpr, asPG);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "swift/AST/DiagnosticEngine.h"
|
||||
#include "swift/AST/Expr.h"
|
||||
#include "swift/AST/Identifier.h"
|
||||
#include "swift/AST/OperatorNameLookup.h"
|
||||
#include "swift/AST/Types.h"
|
||||
#include "swift/Basic/SourceLoc.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
@@ -813,7 +814,7 @@ private:
|
||||
bool exprNeedsParensBeforeAddingAs(const Expr *expr) {
|
||||
auto *DC = getDC();
|
||||
auto asPG = TypeChecker::lookupPrecedenceGroup(
|
||||
DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc());
|
||||
DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()).getSingle();
|
||||
if (!asPG)
|
||||
return true;
|
||||
return exprNeedsParensInsideFollowingOperator(DC, const_cast<Expr *>(expr),
|
||||
@@ -823,7 +824,7 @@ private:
|
||||
bool exprNeedsParensAfterAddingAs(const Expr *expr, const Expr *rootExpr) {
|
||||
auto *DC = getDC();
|
||||
auto asPG = TypeChecker::lookupPrecedenceGroup(
|
||||
DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc());
|
||||
DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc()).getSingle();
|
||||
if (!asPG)
|
||||
return true;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "swift/AST/GenericSignatureBuilder.h"
|
||||
#include "swift/AST/Initializer.h"
|
||||
#include "swift/AST/NameLookup.h"
|
||||
#include "swift/AST/OperatorNameLookup.h"
|
||||
#include "swift/AST/PrettyStackTrace.h"
|
||||
#include "swift/AST/PropertyWrappers.h"
|
||||
#include "swift/AST/ProtocolConformance.h"
|
||||
@@ -1321,21 +1322,6 @@ static void checkPrecedenceCircularity(DiagnosticEngine &D,
|
||||
} while (!stack.empty());
|
||||
}
|
||||
|
||||
static PrecedenceGroupDecl *
|
||||
lookupPrecedenceGroup(const PrecedenceGroupDescriptor &descriptor) {
|
||||
auto *dc = descriptor.dc;
|
||||
if (auto sf = dc->getParentSourceFile()) {
|
||||
auto desc = OperatorLookupDescriptor::forFile(
|
||||
sf, descriptor.ident, dc->isCascadingContextForLookup(false),
|
||||
descriptor.nameLoc);
|
||||
return evaluateOrDefault(sf->getASTContext().evaluator,
|
||||
LookupPrecedenceGroupRequest{desc}, nullptr);
|
||||
} else {
|
||||
return dc->getParentModule()->lookupPrecedenceGroup(descriptor.ident,
|
||||
descriptor.nameLoc);
|
||||
}
|
||||
}
|
||||
|
||||
static PrecedenceGroupDecl *lookupPrecedenceGroupForRelation(
|
||||
DeclContext *dc, PrecedenceGroupDecl::Relation rel,
|
||||
PrecedenceGroupDescriptor::PathDirection direction) {
|
||||
@@ -1350,10 +1336,8 @@ static PrecedenceGroupDecl *lookupPrecedenceGroupForRelation(
|
||||
llvm::handleAllErrors(result.takeError(), [](const Error &E) {});
|
||||
return nullptr;
|
||||
}
|
||||
if (!result.get()) {
|
||||
ctx.Diags.diagnose(rel.NameLoc, diag::unknown_precedence_group, rel.Name);
|
||||
}
|
||||
return result.get();
|
||||
return PrecedenceGroupLookupResult(dc, rel.Name, std::move(*result))
|
||||
.getSingleOrDiagnose(rel.NameLoc);
|
||||
}
|
||||
|
||||
void swift::validatePrecedenceGroup(PrecedenceGroupDecl *PGD) {
|
||||
@@ -1392,10 +1376,8 @@ void swift::validatePrecedenceGroup(PrecedenceGroupDecl *PGD) {
|
||||
// If we didn't find anything, try doing a raw lookup for the group before
|
||||
// diagnosing the 'lowerThan' within the same-module restriction. This can
|
||||
// allow us to diagnose even if we have a precedence group cycle.
|
||||
if (!group) {
|
||||
group = lookupPrecedenceGroup(PrecedenceGroupDescriptor{
|
||||
dc, rel.Name, rel.NameLoc, PrecedenceGroupDescriptor::LowerThan});
|
||||
}
|
||||
if (!group)
|
||||
group = dc->lookupPrecedenceGroup(rel.Name).getSingle();
|
||||
|
||||
if (group &&
|
||||
group->getDeclContext()->getParentModule() == dc->getParentModule()) {
|
||||
@@ -1416,22 +1398,25 @@ void swift::validatePrecedenceGroup(PrecedenceGroupDecl *PGD) {
|
||||
checkPrecedenceCircularity(Diags, PGD);
|
||||
}
|
||||
|
||||
PrecedenceGroupDecl * ValidatePrecedenceGroupRequest::evaluate(
|
||||
TinyPtrVector<PrecedenceGroupDecl *> ValidatePrecedenceGroupRequest::evaluate(
|
||||
Evaluator &eval, PrecedenceGroupDescriptor descriptor) const {
|
||||
if (auto *group = lookupPrecedenceGroup(descriptor)) {
|
||||
auto groups = descriptor.dc->lookupPrecedenceGroup(descriptor.ident);
|
||||
for (auto *group : groups)
|
||||
validatePrecedenceGroup(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
// Return the raw results vector, which will get wrapped back in a
|
||||
// PrecedenceGroupLookupResult by the TypeChecker entry point. This dance
|
||||
// avoids unnecessarily caching the name and context for the lookup.
|
||||
return std::move(groups).get();
|
||||
}
|
||||
|
||||
PrecedenceGroupDecl *TypeChecker::lookupPrecedenceGroup(DeclContext *dc,
|
||||
Identifier name,
|
||||
PrecedenceGroupLookupResult
|
||||
TypeChecker::lookupPrecedenceGroup(DeclContext *dc, Identifier name,
|
||||
SourceLoc nameLoc) {
|
||||
return evaluateOrDefault(
|
||||
auto groups = evaluateOrDefault(
|
||||
dc->getASTContext().evaluator,
|
||||
ValidatePrecedenceGroupRequest({dc, name, nameLoc, None}), nullptr);
|
||||
ValidatePrecedenceGroupRequest({dc, name, nameLoc, None}), {});
|
||||
return PrecedenceGroupLookupResult(dc, name, std::move(groups));
|
||||
}
|
||||
|
||||
static NominalTypeDecl *resolveSingleNominalTypeDecl(
|
||||
@@ -1480,7 +1465,6 @@ OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator,
|
||||
auto enableOperatorDesignatedTypes =
|
||||
ctx.TypeCheckerOpts.EnableOperatorDesignatedTypes;
|
||||
|
||||
auto &Diags = ctx.Diags;
|
||||
PrecedenceGroupDecl *group = nullptr;
|
||||
|
||||
auto identifiers = IOD->getIdentifiers();
|
||||
@@ -1488,22 +1472,20 @@ OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator,
|
||||
auto name = identifiers[0].Item;
|
||||
auto loc = identifiers[0].Loc;
|
||||
|
||||
group = TypeChecker::lookupPrecedenceGroup(dc, name, loc);
|
||||
|
||||
if (group) {
|
||||
identifiers = identifiers.slice(1);
|
||||
} else {
|
||||
// If we're either not allowing types, or we are allowing them
|
||||
// and this identifier is not a type, emit an error as if it's
|
||||
// a precedence group.
|
||||
if (!(enableOperatorDesignatedTypes &&
|
||||
auto canResolveType = [&]() -> bool {
|
||||
return enableOperatorDesignatedTypes &&
|
||||
resolveSingleNominalTypeDecl(dc, loc, name, ctx,
|
||||
TypeResolutionFlags::SilenceErrors))) {
|
||||
Diags.diagnose(loc, diag::unknown_precedence_group, name);
|
||||
TypeResolutionFlags::SilenceErrors);
|
||||
};
|
||||
|
||||
// Make sure not to diagnose in the case where we failed to find a
|
||||
// precedencegroup, but we could resolve a type instead.
|
||||
auto groups = TypeChecker::lookupPrecedenceGroup(dc, name, loc);
|
||||
if (groups.hasResults() || !canResolveType()) {
|
||||
group = groups.getSingleOrDiagnose(loc);
|
||||
identifiers = identifiers.slice(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unless operator designed types are enabled, the parser will ensure that
|
||||
// only one identifier is allowed in the clause, which we should have just
|
||||
@@ -1511,13 +1493,9 @@ OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator,
|
||||
assert(identifiers.empty() || enableOperatorDesignatedTypes);
|
||||
|
||||
if (!group) {
|
||||
group = TypeChecker::lookupPrecedenceGroup(dc, ctx.Id_DefaultPrecedence,
|
||||
SourceLoc());
|
||||
}
|
||||
|
||||
if (!group) {
|
||||
Diags.diagnose(IOD->getLoc(), diag::missing_builtin_precedence_group,
|
||||
ctx.Id_DefaultPrecedence);
|
||||
auto groups = TypeChecker::lookupPrecedenceGroup(
|
||||
dc, ctx.Id_DefaultPrecedence, SourceLoc());
|
||||
group = groups.getSingleOrDiagnose(IOD->getLoc(), /*forBuiltin*/ true);
|
||||
}
|
||||
|
||||
auto nominalTypes = IOD->getDesignatedNominalTypes();
|
||||
@@ -1797,24 +1775,15 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
|
||||
FD->diagnose(diag::operator_in_local_scope);
|
||||
}
|
||||
|
||||
auto desc = OperatorLookupDescriptor::forFile(
|
||||
FD->getDeclContext()->getParentSourceFile(), operatorName,
|
||||
FD->isCascadingContextForLookup(false), FD->getLoc());
|
||||
OperatorDecl *op = nullptr;
|
||||
NullablePtr<OperatorDecl> op;
|
||||
if (FD->isUnaryOperator()) {
|
||||
if (FD->getAttrs().hasAttribute<PrefixAttr>()) {
|
||||
op = evaluateOrDefault(evaluator,
|
||||
LookupPrefixOperatorRequest{desc}, nullptr);
|
||||
op = FD->lookupPrefixOperator(operatorName);
|
||||
} else if (FD->getAttrs().hasAttribute<PostfixAttr>()) {
|
||||
op = evaluateOrDefault(evaluator,
|
||||
LookupPostfixOperatorRequest{desc}, nullptr);
|
||||
op = FD->lookupPostfixOperator(operatorName);
|
||||
} else {
|
||||
auto prefixOp = evaluateOrDefault(evaluator,
|
||||
LookupPrefixOperatorRequest{desc},
|
||||
nullptr);
|
||||
auto postfixOp = evaluateOrDefault(evaluator,
|
||||
LookupPostfixOperatorRequest{desc},
|
||||
nullptr);
|
||||
auto *prefixOp = FD->lookupPrefixOperator(operatorName);
|
||||
auto *postfixOp = FD->lookupPostfixOperator(operatorName);
|
||||
|
||||
// If we found both prefix and postfix, or neither prefix nor postfix,
|
||||
// complain. We can't fix this situation.
|
||||
@@ -1823,9 +1792,11 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
|
||||
|
||||
// If we found both, point at them.
|
||||
if (prefixOp) {
|
||||
diags.diagnose(prefixOp, diag::unary_operator_declaration_here, false)
|
||||
diags.diagnose(prefixOp, diag::unary_operator_declaration_here,
|
||||
/*isPostfix*/ false)
|
||||
.fixItInsert(FD->getLoc(), "prefix ");
|
||||
diags.diagnose(postfixOp, diag::unary_operator_declaration_here, true)
|
||||
diags.diagnose(postfixOp, diag::unary_operator_declaration_here,
|
||||
/*isPostfix*/ true)
|
||||
.fixItInsert(FD->getLoc(), "postfix ");
|
||||
} else {
|
||||
// FIXME: Introduce a Fix-It that adds the operator declaration?
|
||||
@@ -1842,7 +1813,8 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
|
||||
// Fix the AST and determine the insertion text.
|
||||
const char *insertionText;
|
||||
auto &C = FD->getASTContext();
|
||||
if (postfixOp) {
|
||||
auto isPostfix = static_cast<bool>(postfixOp);
|
||||
if (isPostfix) {
|
||||
insertionText = "postfix ";
|
||||
op = postfixOp;
|
||||
FD->getAttrs().add(new (C) PostfixAttr(/*implicit*/false));
|
||||
@@ -1854,15 +1826,20 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
|
||||
|
||||
// Emit diagnostic with the Fix-It.
|
||||
diags.diagnose(FD->getFuncLoc(), diag::unary_op_missing_prepos_attribute,
|
||||
static_cast<bool>(postfixOp))
|
||||
isPostfix)
|
||||
.fixItInsert(FD->getFuncLoc(), insertionText);
|
||||
diags.diagnose(op, diag::unary_operator_declaration_here,
|
||||
static_cast<bool>(postfixOp));
|
||||
op.get()->diagnose(diag::unary_operator_declaration_here, isPostfix);
|
||||
}
|
||||
} else if (FD->isBinaryOperator()) {
|
||||
op = evaluateOrDefault(evaluator,
|
||||
LookupInfixOperatorRequest{desc},
|
||||
nullptr);
|
||||
auto results = FD->lookupInfixOperator(operatorName);
|
||||
|
||||
// If we have an ambiguity, diagnose and return. Otherwise fall through, as
|
||||
// we have a custom diagnostic for missing operator decls.
|
||||
if (results.isAmbiguous()) {
|
||||
results.diagnoseAmbiguity(FD->getLoc());
|
||||
return nullptr;
|
||||
}
|
||||
op = results.getSingle();
|
||||
} else {
|
||||
diags.diagnose(FD, diag::invalid_arg_count_for_operator);
|
||||
return nullptr;
|
||||
@@ -1910,8 +1887,7 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
|
||||
opDiagnostic.fixItInsert(insertionLoc, insertion);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return op;
|
||||
return op.get();
|
||||
}
|
||||
|
||||
bool swift::isMemberOperator(FuncDecl *decl, Type type) {
|
||||
|
||||
@@ -430,9 +430,7 @@ static void checkOperatorOrPrecedenceGroupRedeclaration(
|
||||
|
||||
auto *module = currentFile->getParentModule();
|
||||
auto &ctx = module->getASTContext();
|
||||
auto desc = OperatorLookupDescriptor::forModule(module, decl->getName(),
|
||||
/*cascades*/ true,
|
||||
/*diagLoc*/ SourceLoc());
|
||||
auto desc = OperatorLookupDescriptor::forModule(module, decl->getName());
|
||||
auto otherDecls = lookupOthers(desc);
|
||||
for (auto *other : otherDecls) {
|
||||
if (other == decl || other->isInvalid())
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "TypeChecker.h"
|
||||
#include "swift/AST/NameLookup.h"
|
||||
#include "swift/AST/NameLookupRequests.h"
|
||||
#include "swift/AST/OperatorNameLookup.h"
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/Initializer.h"
|
||||
#include "swift/AST/ParameterList.h"
|
||||
@@ -130,32 +131,18 @@ Expr *TypeChecker::substituteInputSugarTypeForResult(ApplyExpr *E) {
|
||||
static PrecedenceGroupDecl *lookupPrecedenceGroupForOperator(DeclContext *DC,
|
||||
Identifier name,
|
||||
SourceLoc loc) {
|
||||
auto desc = OperatorLookupDescriptor::forFile(
|
||||
DC->getParentSourceFile(), name, DC->isCascadingContextForLookup(true),
|
||||
loc);
|
||||
auto &Ctx = DC->getASTContext();
|
||||
if (auto op = evaluateOrDefault(Ctx.evaluator,
|
||||
LookupInfixOperatorRequest{desc},
|
||||
nullptr)) {
|
||||
return op->getPrecedenceGroup();
|
||||
} else {
|
||||
Ctx.Diags.diagnose(loc, diag::unknown_binop);
|
||||
}
|
||||
return nullptr;
|
||||
auto *op = DC->lookupInfixOperator(name).getSingleOrDiagnose(loc);
|
||||
return op ? op->getPrecedenceGroup() : nullptr;
|
||||
}
|
||||
|
||||
PrecedenceGroupDecl *
|
||||
TypeChecker::lookupPrecedenceGroupForInfixOperator(DeclContext *DC, Expr *E) {
|
||||
/// Look up the builtin precedence group with the given name.
|
||||
|
||||
auto getBuiltinPrecedenceGroup = [](DeclContext *DC, Identifier name,
|
||||
SourceLoc loc) {
|
||||
auto group = TypeChecker::lookupPrecedenceGroup(DC, name, loc);
|
||||
if (!group) {
|
||||
DC->getASTContext().Diags.diagnose(
|
||||
loc, diag::missing_builtin_precedence_group, name);
|
||||
}
|
||||
return group;
|
||||
auto getBuiltinPrecedenceGroup = [&](DeclContext *DC, Identifier name,
|
||||
SourceLoc loc) -> PrecedenceGroupDecl * {
|
||||
auto groups = TypeChecker::lookupPrecedenceGroup(DC, name, loc);
|
||||
return groups.getSingleOrDiagnose(loc, /*forBuiltin*/ true);
|
||||
};
|
||||
|
||||
auto &Context = DC->getASTContext();
|
||||
|
||||
@@ -1012,8 +1012,8 @@ LookupResult lookupConstructors(
|
||||
PrecedenceGroupDecl *lookupPrecedenceGroupForInfixOperator(DeclContext *dc,
|
||||
Expr *op);
|
||||
|
||||
PrecedenceGroupDecl *lookupPrecedenceGroup(DeclContext *dc, Identifier name,
|
||||
SourceLoc nameLoc);
|
||||
PrecedenceGroupLookupResult
|
||||
lookupPrecedenceGroup(DeclContext *dc, Identifier name, SourceLoc nameLoc);
|
||||
|
||||
/// Check whether the given declaration can be written as a
|
||||
/// member of the given base type.
|
||||
|
||||
@@ -2247,9 +2247,6 @@ OperatorDecl *ModuleFile::lookupOperator(Identifier name,
|
||||
if (getStableFixity(fixity) == item.first)
|
||||
return cast<OperatorDecl>(getDecl(item.second));
|
||||
}
|
||||
|
||||
// FIXME: operators re-exported from other modules?
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
5
test/decl/operator/Inputs/lookup_other_noncompat.swift
Normal file
5
test/decl/operator/Inputs/lookup_other_noncompat.swift
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
// Also declared in the files being tested.
|
||||
precedencegroup RedeclaredInModule {}
|
||||
// expected-note@-1 {{previous precedence group declaration here}}
|
||||
// expected-note@-2 {{found this matching precedence group}}
|
||||
10
test/decl/operator/Inputs/redeclaration_other.swift
Normal file
10
test/decl/operator/Inputs/redeclaration_other.swift
Normal file
@@ -0,0 +1,10 @@
|
||||
precedencegroup RedeclaredAcrossFiles {} // expected-note {{previous precedence group declaration here}}
|
||||
|
||||
infix operator ^^^ // expected-note {{previous operator declaration here}}
|
||||
prefix operator >>> // expected-note {{previous operator declaration here}}
|
||||
postfix operator <<< // expected-note {{previous operator declaration here}}
|
||||
|
||||
precedencegroup P1 {}
|
||||
infix operator ^^^^ : P1 // expected-note {{previous operator declaration here}}
|
||||
|
||||
infix operator &&&
|
||||
113
test/decl/operator/lookup.swift
Normal file
113
test/decl/operator/lookup.swift
Normal file
@@ -0,0 +1,113 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_moduleD.swift -module-name D -o %t -I %t
|
||||
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_moduleC.swift -module-name C -o %t -I %t
|
||||
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_moduleB.swift -module-name B -o %t -I %t
|
||||
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_moduleA.swift -module-name A -o %t -I %t
|
||||
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_module_exportsAC.swift -module-name ExportsAC -o %t -I %t
|
||||
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s %S/Inputs/lookup_other.swift %S/Inputs/lookup_other2.swift %S/Inputs/lookup_other_noncompat.swift -I %t -enable-new-operator-lookup
|
||||
|
||||
import ExportsAC
|
||||
import B
|
||||
|
||||
infix operator ^^^ : DeclaredAcrossFiles
|
||||
func ^^^ (lhs: Int, rhs: Int) -> Int { 0 }
|
||||
func &&& (lhs: Int, rhs: Int) -> Int { 0 }
|
||||
|
||||
// The operator decl >>> is declared in module A, which we should be able to
|
||||
// see through ExportsAC.
|
||||
prefix func >>> (rhs: Double) {}
|
||||
|
||||
// Make sure we can also see precedencegroups in module A through ExportsAC.
|
||||
infix operator ^^^^ : DeclaredInModuleA
|
||||
|
||||
// The operator decl for ??? is declared in both modules A and B, but has the
|
||||
// same default precedence group in both, so there's no ambiguity.
|
||||
func ??? (lhs: Int, rhs: Int) {}
|
||||
|
||||
// Same for ???!, declared in modules ExportsAC and B, but has the same
|
||||
// precedence group in both.
|
||||
func ???! (lhs: Int, rhs: Int) {}
|
||||
|
||||
// The operator decl for ???? is declared in both modules A and B, and has a
|
||||
// different precedence group in each. Therefore ambiguous.
|
||||
func ???? (lhs: Int, rhs: Int) {} // expected-error {{ambiguous operator declarations found for operator}}
|
||||
|
||||
// Same for ????!, declared in both modules ExportsAC and B, and has a different
|
||||
// precedence group in each. Therefore ambiguous.
|
||||
func ????! (lhs: Int, rhs: Int) {} // expected-error {{ambiguous operator declarations found for operator}}
|
||||
|
||||
// The precedencegroup is declared in both modules A and B, therefore ambiguous.
|
||||
infix operator <?> : DeclaredInModulesAB // expected-error {{multiple precedence groups found}}
|
||||
|
||||
// The precedencegroup is declared in both modules ExportsAC and B, therefore
|
||||
// ambiguous.
|
||||
infix operator <!> : DeclaredInModulesBExportsAC // expected-error {{multiple precedence groups found}}
|
||||
|
||||
// While this precedencegroup is declared in both modules A and B, it's also
|
||||
// declared in this module, which therefore shadows those decls.
|
||||
infix operator <??> : DeclaredInModulesABShadowed
|
||||
|
||||
// The operator decl for <? is declared in both modules A and B, but there's no
|
||||
// meaningful difference between the declarations, so legal.
|
||||
postfix func <? (lhs: Int) {}
|
||||
|
||||
// Same thing, <! is declared in both modules ExportsAC and B, but there's no
|
||||
// meaningful difference between the declarations, so legal.
|
||||
postfix func <! (lhs: Int) {}
|
||||
|
||||
// This precedencegroup is declared in both modules A and ExportsAC, but the
|
||||
// latter shadows the former.
|
||||
infix operator <???> : ShadowsModuleA
|
||||
|
||||
// This precedencegroup is declared in modules A, C, and ExportsAC, but the
|
||||
// latter shadows both of the former.
|
||||
infix operator <????> : ShadowsModulesAC
|
||||
|
||||
// This operator decl is declared in modules A, C, and ExportsAC, but the
|
||||
// latter shadows both of the former.
|
||||
func ????? (lhs: Int, rhs: Int) {}
|
||||
|
||||
// This operator decl is declared in modules A, C, and ExportsAC, but the
|
||||
// latter shadows both of the former, despite them having different
|
||||
// precedencegroups.
|
||||
func ?????? (lhs: Int, rhs: Int) {}
|
||||
|
||||
// Module D is imported through exports in both lookup_other and lookup_other2.
|
||||
// Make sure we correctly handle visiting the same module twice.
|
||||
infix operator <> : DeclaredInModuleD
|
||||
|
||||
// Also declared in lookup_other.
|
||||
precedencegroup RedeclaredInModule {}
|
||||
// expected-error@-1 {{precedence group redeclared}}
|
||||
// expected-note@-2 {{found this matching precedence group}}
|
||||
|
||||
infix operator *** : RedeclaredInModule // expected-error {{multiple precedence groups found}}
|
||||
|
||||
func testOperatorLookup() {
|
||||
// In lookup_other, DeclaredAcrossFiles is left associative, whereas in
|
||||
// module B it is non-associative. Make sure we use lookup_other's.
|
||||
_ = 5 ^^^ 5 ^^^ 5 // Okay.
|
||||
|
||||
// Same for &&&, in lookup_other it is declared as left associative.
|
||||
_ = 5 &&& 5 &&& 5
|
||||
|
||||
// The operator >>> is declared in module A, which we should be able to see
|
||||
// through ExportsAC.
|
||||
>>>1
|
||||
|
||||
// We've been evil and overriden TernaryPrecedence in both modules A and B.
|
||||
// Make sure we emit an ambiguity error without emitting a 'broken stdlib'
|
||||
// error.
|
||||
true ? () : () // expected-error {{multiple precedence groups found}}
|
||||
}
|
||||
|
||||
precedencegroup CastingPrecedence {
|
||||
lowerThan: AssignmentPrecedence
|
||||
}
|
||||
|
||||
func testBuiltinPrecedenceGroupOverriding() {
|
||||
// Evil, but allowed.
|
||||
var x = 0
|
||||
x = 0 as Int // expected-error {{cannot convert value of type '()' to type 'Int' in coercion}}
|
||||
}
|
||||
@@ -14,13 +14,15 @@ infix operator ^^^ : DeclaredAcrossFiles
|
||||
func ^^^ (lhs: Int, rhs: Int) -> Int { 0 }
|
||||
func &&& (lhs: Int, rhs: Int) -> Int { 0 }
|
||||
|
||||
// FIXME(SR-12132): The operator decl >>> is declared in module A, which we
|
||||
// should be able to see through ExportsAC.
|
||||
prefix func >>> (rhs: Double) {} // expected-error {{operator implementation without matching operator declaration}}
|
||||
// The operator decl >>> is declared in module A, which we should be able to
|
||||
// see through ExportsAC. Note that this is possible even with the compatibility
|
||||
// behavior as we can use the new lookup logic when the result is unambiguous.
|
||||
prefix func >>> (rhs: Double) {}
|
||||
|
||||
// FIXME(SR-12132): We should also see precedencegroups in module A through
|
||||
// ExportsAC.
|
||||
infix operator ^^^^ : DeclaredInModuleA // expected-error {{unknown precedence group 'DeclaredInModuleA'}}
|
||||
// Make sure we can also see precedencegroups in module A through ExportsAC.
|
||||
// Note that this is possible even with the compatibility behavior as we can use
|
||||
// the new lookup logic when the result is unambiguous.
|
||||
infix operator ^^^^ : DeclaredInModuleA
|
||||
|
||||
// The operator decl for ??? is declared in both modules A and B, but has the
|
||||
// same default precedence group in both, so there's no ambiguity.
|
||||
@@ -38,9 +40,7 @@ func ???? (lhs: Int, rhs: Int) {}
|
||||
|
||||
// The operator decl for ????! is declared in both modules ExportsAC and B, and
|
||||
// has a different precedence group in each. Therefore ambiguous.
|
||||
// FIXME: We shouldn't emit the unknown operator decl error.
|
||||
func ????! (lhs: Int, rhs: Int) {} // expected-error {{ambiguous operator declarations found for operator}}
|
||||
// expected-error@-1 {{operator implementation without matching operator declaration}}
|
||||
|
||||
// Same as ????, the precedencegroup is declared in both modules A and B, but
|
||||
// we don't look into module A for compatibility.
|
||||
@@ -48,9 +48,7 @@ infix operator <?> : DeclaredInModulesAB
|
||||
|
||||
// The precedencegroup is declared in both modules ExportsAC and B, therefore
|
||||
// ambiguous.
|
||||
// FIXME: We shouldn't emit the 'unknown precedence group' error.
|
||||
infix operator <!> : DeclaredInModulesBExportsAC // expected-error {{multiple precedence groups found}}
|
||||
// expected-error@-1 {{unknown precedence group 'DeclaredInModulesBExportsAC'}}
|
||||
|
||||
// This precedencegroup is declared in this module as well as in both modules A
|
||||
// and B. The decl in this module should shadow the imported ones, but for
|
||||
@@ -82,10 +80,9 @@ func ????? (lhs: Int, rhs: Int) {}
|
||||
// precedencegroups.
|
||||
func ?????? (lhs: Int, rhs: Int) {}
|
||||
|
||||
// FIXME: Module D is imported through exports in both lookup_other and
|
||||
// lookup_other2, but we fail to detect the fact that we're visiting the same
|
||||
// thing twice.
|
||||
infix operator <> : DeclaredInModuleD // expected-error {{unknown precedence group 'DeclaredInModuleD'}}
|
||||
// Module D is imported through exports in both lookup_other and lookup_other2.
|
||||
// Make sure we correctly handle visiting the same module twice.
|
||||
infix operator <> : DeclaredInModuleD
|
||||
|
||||
// Also declared in lookup_other. To preserve compatibility, we allow an
|
||||
// unambiguous lookup that will favor this declaration over lookup_other.
|
||||
@@ -109,9 +106,9 @@ func testOperatorLookup() {
|
||||
>>>1
|
||||
|
||||
// We've been evil and overriden TernaryPrecedence in both modules A and B.
|
||||
// FIXME: We shouldn't emit the 'broken stdlib' error.
|
||||
// Make sure we emit an ambiguity error without emitting a 'broken stdlib'
|
||||
// error.
|
||||
true ? () : () // expected-error {{multiple precedence groups found}}
|
||||
// expected-error@-1 {{broken standard library: missing builtin precedence group 'TernaryPrecedence'}}
|
||||
}
|
||||
|
||||
precedencegroup CastingPrecedence {
|
||||
|
||||
31
test/decl/operator/redeclaration.swift
Normal file
31
test/decl/operator/redeclaration.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s %S/Inputs/redeclaration_other.swift -enable-new-operator-lookup
|
||||
|
||||
precedencegroup RedeclaredAcrossFiles {} // expected-error {{precedence group redeclared}}
|
||||
|
||||
precedencegroup RedeclaredSameFile {} // expected-note {{previous precedence group declaration here}}
|
||||
precedencegroup RedeclaredSameFile {} // expected-error {{precedence group redeclared}}
|
||||
|
||||
precedencegroup RedeclaredSameFile2 { // expected-note {{previous precedence group declaration here}}
|
||||
assignment: true
|
||||
}
|
||||
precedencegroup RedeclaredSameFile2 {} // expected-error {{precedence group redeclared}}
|
||||
|
||||
// These are all declared in the other file.
|
||||
infix operator ^^^ // expected-error {{operator redeclared}}
|
||||
prefix operator >>> // expected-error {{operator redeclared}}
|
||||
postfix operator <<< // expected-error {{operator redeclared}}
|
||||
infix operator ^^^^ // expected-error {{operator redeclared}}
|
||||
|
||||
// This is declared as an infix operator in the other file, so no problem.
|
||||
prefix operator &&&
|
||||
postfix operator &&&
|
||||
|
||||
infix operator %%% // expected-note {{previous operator declaration here}}
|
||||
infix operator %%% // expected-error {{operator redeclared}}
|
||||
|
||||
prefix operator %%% // expected-note {{previous operator declaration here}}
|
||||
prefix operator %%% // expected-error {{operator redeclared}}
|
||||
|
||||
precedencegroup P2 {}
|
||||
infix operator *** : P2 // expected-note {{previous operator declaration here}}
|
||||
infix operator *** // expected-error {{operator redeclared}}
|
||||
Reference in New Issue
Block a user