Merge pull request #1156 from tkremenek/SR-650
[Swift 2.2] fix SR-650: REGRESSION: Assertion failed
diff --git a/CHANGELOG.md b/CHANGELOG.md
index edc11a0..c6baa9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -142,6 +142,29 @@
For more information, see [SE-0020](https://github.com/apple/swift-evolution/blob/master/proposals/0020-if-swift-version.md).
+* The Objective-C selector of a Swift method can now be determined
+ directly with the #selector expression, e.g.,:
+
+ let sel = #selector(insertSubview(_:aboveSubview:)) // sel has type Selector
+
+ Along with this change, the use of string literals as selectors has
+ been deprecated, e.g.,
+
+ let sel: Selector = "insertSubview:aboveSubview:"
+
+ Generally, such string literals should be replaced with uses of
+ `#selector`, and the compiler will provide Fix-Its that use
+ `#selector`. In cases where they is not possible (e.g., when referring
+ to the getter of a property), one can still directly construct
+ selectors, e.g.,:
+
+ let sel = Selector("propertyName")
+
+ Note that the compiler is now checking the string literals used to
+ construct Selectors to ensure that they are well-formed Objective-C
+ selectors and that there is an '@objc' method with that selector.
+
+
2015-09-17 [Xcode 7.1, Swift 2.1]
----------
diff --git a/docs/SIL.rst b/docs/SIL.rst
index 0b1232f..68c8760 100644
--- a/docs/SIL.rst
+++ b/docs/SIL.rst
@@ -2452,6 +2452,7 @@
sil-instruction ::= 'string_literal' encoding string-literal
encoding ::= 'utf8'
encoding ::= 'utf16'
+ encoding ::= 'objc_selector'
%1 = string_literal "asdf"
// %1 has type $Builtin.RawPointer
@@ -2459,7 +2460,10 @@
Creates a reference to a string in the global string table. The result
is a pointer to the data. The referenced string is always null-terminated. The
string literal value is specified using Swift's string
-literal syntax (though ``\()`` interpolations are not allowed).
+literal syntax (though ``\()`` interpolations are not allowed). When
+the encoding is ``objc_selector``, the string literal produces a
+reference to a UTF-8-encoded Objective-C selector in the Objective-C
+method name segment.
Dynamic Dispatch
~~~~~~~~~~~~~~~~
diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h
index a131ad4..34dbb0a 100644
--- a/include/swift/AST/DeclContext.h
+++ b/include/swift/AST/DeclContext.h
@@ -405,6 +405,12 @@
LazyResolver *typeResolver,
SmallVectorImpl<ValueDecl *> &decls) const;
+ /// Look up all Objective-C methods with the given selector visible
+ /// in the enclosing module.
+ void lookupAllObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const;
+
/// Return the ASTContext for a specified DeclContext by
/// walking up to the enclosing module and returning its ASTContext.
ASTContext &getASTContext() const;
diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def
index 508e07b..e085d87 100644
--- a/include/swift/AST/DiagnosticsParse.def
+++ b/include/swift/AST/DiagnosticsParse.def
@@ -1017,6 +1017,14 @@
ERROR(string_interpolation_extra,none,
"extra tokens after interpolated string expression", ())
+// Selector expressions.
+ERROR(expr_selector_expected_lparen,PointsToFirstBadToken,
+ "expected '(' following '#selector'", ())
+ERROR(expr_selector_expected_expr,PointsToFirstBadToken,
+ "expected expression naming a method within '#selector(...)'", ())
+ERROR(expr_selector_expected_rparen,PointsToFirstBadToken,
+ "expected ')' to complete '#selector' expression", ())
+
//------------------------------------------------------------------------------
// Attribute-parsing diagnostics
//------------------------------------------------------------------------------
diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def
index fa0c6b1..f202e89 100644
--- a/include/swift/AST/DiagnosticsSIL.def
+++ b/include/swift/AST/DiagnosticsSIL.def
@@ -88,6 +88,9 @@
"C function pointer signature %0 is not compatible with expected type %1",
(Type, Type))
+ERROR(objc_selector_malformed,none,"the type ObjectiveC.Selector is malformed",
+ ())
+
// Definite initialization diagnostics.
NOTE(variable_defined_here,none,
"%select{variable|constant}0 defined here", (bool))
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index f7a4448..7058ad3 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -336,8 +336,38 @@
"invalid conversion from non-escaping function of type %0 to "
"potentially escaping function type %1", (Type, Type))
+// Selector expressions.
+ERROR(expr_selector_no_objc_runtime,none,
+ "'#selector' can only be used with the Objective-C runtime", ())
+ERROR(expr_selector_module_missing,none,
+ "import the 'ObjectiveC' module to use '#selector'", ())
+ERROR(expr_selector_no_declaration,none,
+ "argument of '#selector' does not refer to an initializer or method", ())
+ERROR(expr_selector_property,none,
+ "argument of '#selector' cannot refer to a property", ())
+ERROR(expr_selector_not_method_or_init,none,
+ "argument of '#selector' does not refer to a method or initializer", ())
+ERROR(expr_selector_not_objc,none,
+ "argument of '#selector' refers to %select{a method|an initializer}0 "
+ "that is not exposed to Objective-C",
+ (bool))
+NOTE(expr_selector_make_objc,none,
+ "add '@objc' to expose this %select{method|initializer}0 to Objective-C",
+ (bool))
-
+// Selectors-as-string-literals.
+WARNING(selector_literal_invalid,none,
+ "string literal is not a valid Objective-C selector", ())
+WARNING(selector_literal_undeclared,none,
+ "no method declared with Objective-C selector %0", (ObjCSelector))
+WARNING(selector_literal_deprecated,none,
+ "use of string literal for Objective-C selectors is deprecated; "
+ "use '#selector' or explicitly construct a 'Selector'", ())
+WARNING(selector_literal_deprecated_suggest,none,
+ "use of string literal for Objective-C selectors is deprecated; "
+ "use '#selector' instead", ())
+WARNING(selector_construction_suggest,none,
+ "use '#selector' instead of explicitly constructing a 'Selector'", ())
ERROR(cannot_return_value_from_void_func,none,
"unexpected non-void return value in void function", ())
diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h
index cd882e3..a5da8f5 100644
--- a/include/swift/AST/Expr.h
+++ b/include/swift/AST/Expr.h
@@ -3679,6 +3679,44 @@
void setSemanticExpr(Expr *SE) { SemanticExpr = SE; }
};
+/// Produces the Objective-C selector of the referenced method.
+///
+/// \code
+/// #selector(UIView.insertSubview(_:aboveSubview:))
+/// \endcode
+class ObjCSelectorExpr : public Expr {
+ SourceLoc KeywordLoc;
+ SourceLoc LParenLoc;
+ Expr *SubExpr;
+ SourceLoc RParenLoc;
+ AbstractFunctionDecl *Method = nullptr;
+
+public:
+ ObjCSelectorExpr(SourceLoc keywordLoc, SourceLoc lParenLoc,
+ Expr *subExpr, SourceLoc rParenLoc)
+ : Expr(ExprKind::ObjCSelector, /*Implicit=*/false),
+ KeywordLoc(keywordLoc), LParenLoc(lParenLoc), SubExpr(subExpr),
+ RParenLoc(rParenLoc) { }
+
+ Expr *getSubExpr() const { return SubExpr; }
+ void setSubExpr(Expr *expr) { SubExpr = expr; }
+
+ /// Retrieve the Objective-C method to which this expression refers.
+ AbstractFunctionDecl *getMethod() const { return Method; }
+
+ /// Set the Objective-C method to which this expression refers.
+ void setMethod(AbstractFunctionDecl *method) { Method = method; }
+
+ SourceLoc getLoc() const { return KeywordLoc; }
+ SourceRange getSourceRange() const {
+ return SourceRange(KeywordLoc, RParenLoc);
+ }
+
+ static bool classof(const Expr *E) {
+ return E->getKind() == ExprKind::ObjCSelector;
+ }
+};
+
#undef SWIFT_FORWARD_SOURCE_LOCS_TO
} // end namespace swift
diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def
index f378930..66387b9 100644
--- a/include/swift/AST/ExprNodes.def
+++ b/include/swift/AST/ExprNodes.def
@@ -155,6 +155,7 @@
EXPR(CodeCompletion, Expr)
UNCHECKED_EXPR(UnresolvedPattern, Expr)
EXPR(EditorPlaceholder, Expr)
+EXPR(ObjCSelector, Expr)
#undef EXPR_RANGE
#undef UNCHECKED_EXPR
diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def
index 1e5393f..fed0999 100644
--- a/include/swift/AST/KnownIdentifiers.def
+++ b/include/swift/AST/KnownIdentifiers.def
@@ -50,6 +50,7 @@
IDENTIFIER(Protocol)
IDENTIFIER(rawValue)
IDENTIFIER(RawValue)
+IDENTIFIER(Selector)
IDENTIFIER(self)
IDENTIFIER(Self)
IDENTIFIER(setObject)
diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h
index ec8b5cc..9636087 100644
--- a/include/swift/AST/Module.h
+++ b/include/swift/AST/Module.h
@@ -409,6 +409,11 @@
DeclContext *container, DeclName name,
Identifier privateDiscriminator) const;
+ /// Find all Objective-C methods with the given selector.
+ void lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const;
+
/// \sa getImportedModules
enum class ImportFilter {
All,
@@ -639,6 +644,11 @@
DeclName name,
SmallVectorImpl<ValueDecl*> &results) const {}
+ /// Find all Objective-C methods with the given selector.
+ virtual void lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const = 0;
+
/// Returns the comment attached to the given declaration.
///
/// This function is an implementation detail for comment serialization.
@@ -804,6 +814,10 @@
void getTopLevelDecls(SmallVectorImpl<Decl*> &results) const override;
+ void lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const override;
+
Identifier
getDiscriminatorForPrivateValue(const ValueDecl *D) const override {
llvm_unreachable("no private decls in the derived file unit");
@@ -903,6 +917,11 @@
/// complete, we diagnose.
std::map<DeclAttrKind, const DeclAttribute *> AttrsRequiringFoundation;
+ /// A mapping from Objective-C selectors to the methods that have
+ /// those selectors.
+ llvm::DenseMap<ObjCSelector, llvm::TinyPtrVector<AbstractFunctionDecl *>>
+ ObjCMethods;
+
template <typename T>
using OperatorMap = llvm::DenseMap<Identifier,llvm::PointerIntPair<T,1,bool>>;
@@ -959,6 +978,10 @@
lookupClassMember(ModuleDecl::AccessPathTy accessPath, DeclName name,
SmallVectorImpl<ValueDecl*> &results) const override;
+ void lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const override;
+
virtual void getTopLevelDecls(SmallVectorImpl<Decl*> &results) const override;
virtual void
@@ -1120,6 +1143,11 @@
NLKind lookupKind,
SmallVectorImpl<ValueDecl*> &result) const override;
+ /// Find all Objective-C methods with the given selector.
+ void lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const override;
+
Identifier
getDiscriminatorForPrivateValue(const ValueDecl *D) const override {
llvm_unreachable("no private values in the Builtin module");
diff --git a/include/swift/ClangImporter/ClangModule.h b/include/swift/ClangImporter/ClangModule.h
index 8fcc9f5..c12eef8 100644
--- a/include/swift/ClangImporter/ClangModule.h
+++ b/include/swift/ClangImporter/ClangModule.h
@@ -73,6 +73,10 @@
lookupClassMember(ModuleDecl::AccessPathTy accessPath, DeclName name,
SmallVectorImpl<ValueDecl*> &decls) const override;
+ void lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const override;
+
virtual void getTopLevelDecls(SmallVectorImpl<Decl*> &results) const override;
virtual void getDisplayDecls(SmallVectorImpl<Decl*> &results) const override;
diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h
index dee2702..47e0283 100644
--- a/include/swift/Parse/Parser.h
+++ b/include/swift/Parse/Parser.h
@@ -1097,6 +1097,7 @@
bool isExprBasic);
ParserResult<Expr> parseExprPostfix(Diag<> ID, bool isExprBasic);
ParserResult<Expr> parseExprUnary(Diag<> ID, bool isExprBasic);
+ ParserResult<Expr> parseExprSelector();
ParserResult<Expr> parseExprSuper();
ParserResult<Expr> parseExprConfiguration();
Expr *parseExprStringLiteral();
diff --git a/include/swift/Parse/Token.h b/include/swift/Parse/Token.h
index 4107cd7..a6f2a3a 100644
--- a/include/swift/Parse/Token.h
+++ b/include/swift/Parse/Token.h
@@ -44,6 +44,7 @@
pound_endif,
pound_line,
pound_available,
+ pound_selector,
comment,
#define KEYWORD(X) kw_ ## X,
diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h
index 2d177fb..367383c 100644
--- a/include/swift/SIL/SILInstruction.h
+++ b/include/swift/SIL/SILInstruction.h
@@ -1254,7 +1254,9 @@
public:
enum class Encoding {
UTF8,
- UTF16
+ UTF16,
+ /// UTF-8 encoding of an Objective-C selector.
+ ObjCSelector,
};
private:
diff --git a/include/swift/Serialization/ModuleFile.h b/include/swift/Serialization/ModuleFile.h
index 78bbbdb..fbf4181 100644
--- a/include/swift/Serialization/ModuleFile.h
+++ b/include/swift/Serialization/ModuleFile.h
@@ -553,6 +553,11 @@
DeclName name,
SmallVectorImpl<ValueDecl*> &results);
+ /// Find all Objective-C methods with the given selector.
+ void lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results);
+
/// Reports all link-time dependencies.
void collectLinkLibraries(Module::LinkLibraryCallback callback) const;
diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h
index ac19024..174e2c4 100644
--- a/include/swift/Serialization/SerializedModuleLoader.h
+++ b/include/swift/Serialization/SerializedModuleLoader.h
@@ -131,6 +131,11 @@
lookupClassMember(Module::AccessPathTy accessPath, DeclName name,
SmallVectorImpl<ValueDecl*> &decls) const override;
+ /// Find all Objective-C methods with the given selector.
+ void lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const override;
+
Optional<BriefAndRawComment> getCommentForDecl(const Decl *D) const override;
virtual void getTopLevelDecls(SmallVectorImpl<Decl*> &results) const override;
diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp
index 3ab4821..c0e8d7e 100644
--- a/lib/AST/ASTDumper.cpp
+++ b/lib/AST/ASTDumper.cpp
@@ -2161,6 +2161,16 @@
}
OS << ')';
}
+ void visitObjCSelectorExpr(ObjCSelectorExpr *E) {
+ printCommon(E, "objc_selector_expr") << " decl=";
+ if (auto method = E->getMethod())
+ method->dumpRef(OS);
+ else
+ OS << "<unresolved>";
+ OS << '\n';
+ printRec(E->getSubExpr());
+ OS << ')';
+ }
};
} // end anonymous namespace.
diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp
index 15ea22a..5ee65cf 100644
--- a/lib/AST/ASTWalker.cpp
+++ b/lib/AST/ASTWalker.cpp
@@ -811,6 +811,14 @@
return E;
}
+ Expr *visitObjCSelectorExpr(ObjCSelectorExpr *E) {
+ Expr *sub = doIt(E->getSubExpr());
+ if (!sub) return nullptr;
+
+ E->setSubExpr(sub);
+ return E;
+ }
+
//===--------------------------------------------------------------------===//
// Everything Else
//===--------------------------------------------------------------------===//
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index bad8808..7163581 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -314,6 +314,7 @@
NON_LVALUE_EXPR(Assign)
NON_LVALUE_EXPR(DefaultValue)
NON_LVALUE_EXPR(CodeCompletion)
+ NON_LVALUE_EXPR(ObjCSelector)
#define UNCHECKED_EXPR(KIND, BASE) \
NON_LVALUE_EXPR(KIND)
@@ -487,6 +488,7 @@
case ExprKind::StringLiteral:
case ExprKind::InterpolatedStringLiteral:
case ExprKind::MagicIdentifierLiteral:
+ case ExprKind::ObjCSelector:
return true;
case ExprKind::ObjectLiteral:
diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp
index 3d4c24d..387c801 100644
--- a/lib/AST/Module.cpp
+++ b/lib/AST/Module.cpp
@@ -519,12 +519,24 @@
}
}
+void Module::lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const {
+ FORWARD(lookupObjCMethods, (selector, results));
+}
+
void BuiltinUnit::lookupValue(Module::AccessPathTy accessPath, DeclName name,
NLKind lookupKind,
SmallVectorImpl<ValueDecl*> &result) const {
getCache().lookupValue(name.getBaseName(), lookupKind, *this, result);
}
+void BuiltinUnit::lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const {
+ // No @objc methods in the Builtin module.
+}
+
DerivedFileUnit::DerivedFileUnit(Module &M)
: FileUnit(FileUnitKind::Derived, M) {
M.getASTContext().addDestructorCleanup(*this);
@@ -561,6 +573,17 @@
}
}
+void DerivedFileUnit::lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const {
+ for (auto D : DerivedDecls) {
+ if (auto func = dyn_cast<AbstractFunctionDecl>(D)) {
+ if (func->isObjC() && func->getObjCSelector() == selector)
+ results.push_back(func);
+ }
+ }
+}
+
void DerivedFileUnit::getTopLevelDecls(SmallVectorImpl<swift::Decl *> &results)
const {
results.append(DerivedDecls.begin(), DerivedDecls.end());
@@ -606,6 +629,15 @@
getCache().lookupClassMember(accessPath, name, results, *this);
}
+void SourceFile::lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const {
+ // FIXME: Make sure this table is complete, somehow.
+ auto known = ObjCMethods.find(selector);
+ if (known == ObjCMethods.end()) return;
+ results.append(known->second.begin(), known->second.end());
+}
+
void Module::getLocalTypeDecls(SmallVectorImpl<TypeDecl*> &Results) const {
FORWARD(getLocalTypeDecls, (Results));
}
diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp
index cb4fdf6..c031577 100644
--- a/lib/AST/NameLookup.cpp
+++ b/lib/AST/NameLookup.cpp
@@ -1374,3 +1374,21 @@
// We're done. Report success/failure.
return !decls.empty();
}
+
+void DeclContext::lookupAllObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const {
+ // Collect all of the methods with this selector.
+ forAllVisibleModules(this, [&](Module::ImportedModule import) {
+ import.second->lookupObjCMethods(selector, results);
+ });
+
+ // Filter out duplicates.
+ llvm::SmallPtrSet<AbstractFunctionDecl *, 8> visited;
+ results.erase(
+ std::remove_if(results.begin(), results.end(),
+ [&](AbstractFunctionDecl *func) -> bool {
+ return !visited.insert(func).second;
+ }),
+ results.end());
+}
diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp
index bcdb625..9877756 100644
--- a/lib/ClangImporter/ClangImporter.cpp
+++ b/lib/ClangImporter/ClangImporter.cpp
@@ -3809,6 +3809,59 @@
}
}
+void ClangModuleUnit::lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const {
+ // FIXME: Ignore submodules, which are empty for now.
+ if (clangModule && clangModule->isSubModule())
+ return;
+
+ // Map the selector into a Clang selector.
+ auto clangSelector = owner.Impl.exportSelector(selector);
+ if (clangSelector.isNull()) return;
+
+ // Collect all of the Objective-C methods with this selector.
+ SmallVector<clang::ObjCMethodDecl *, 8> objcMethods;
+ auto &clangSema = owner.Impl.getClangSema();
+ clangSema.CollectMultipleMethodsInGlobalPool(clangSelector,
+ objcMethods,
+ /*instance=*/true);
+ clangSema.CollectMultipleMethodsInGlobalPool(clangSelector,
+ objcMethods,
+ /*instance=*/false);
+
+ // Import the methods.
+ auto &clangCtx = clangSema.getASTContext();
+ for (auto objcMethod : objcMethods) {
+ // Verify that this method came from this module.
+ auto owningClangModule = getClangOwningModule(objcMethod, clangCtx);
+ if (owningClangModule)
+ owningClangModule = owningClangModule->getTopLevelModule();
+
+ if (owningClangModule != clangModule) continue;
+
+ // If we found a property accessor, import the property.
+ if (objcMethod->isPropertyAccessor())
+ (void)owner.Impl.importDecl(objcMethod->findPropertyDecl(true));
+
+ // Import it.
+ // FIXME: Retrying a failed import works around recursion bugs in the Clang
+ // importer.
+ auto imported = owner.Impl.importDecl(objcMethod);
+ if (!imported) imported = owner.Impl.importDecl(objcMethod);
+ if (!imported) continue;
+
+ if (auto func = dyn_cast<AbstractFunctionDecl>(imported))
+ results.push_back(func);
+
+ // If there is an alternate declaration, also look at it.
+ if (auto alternate = owner.Impl.getAlternateDecl(imported)) {
+ if (auto func = dyn_cast<AbstractFunctionDecl>(alternate))
+ results.push_back(func);
+ }
+ }
+}
+
void ClangModuleUnit::collectLinkLibraries(
Module::LinkLibraryCallback callback) const {
if (!clangModule)
diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp
index 6e63839..c2f8173 100644
--- a/lib/IDE/CodeCompletion.cpp
+++ b/lib/IDE/CodeCompletion.cpp
@@ -1985,6 +1985,21 @@
Builder.addRightParen();
}
+ void addPoundSelector() {
+ // #selector is only available when the Objective-C runtime is.
+ if (!Ctx.LangOpts.EnableObjCInterop) return;
+
+ CodeCompletionResultBuilder Builder(
+ Sink,
+ CodeCompletionResult::ResultKind::Keyword,
+ SemanticContextKind::ExpressionSpecific,
+ ExpectedTypes);
+ Builder.addTextChunk("selector");
+ Builder.addLeftParen();
+ Builder.addSimpleTypedParameter("@objc method", /*isVarArg=*/false);
+ Builder.addRightParen();
+ }
+
void addFunctionCallPattern(const AnyFunctionType *AFT,
const AbstractFunctionDecl *AFD = nullptr) {
foundFunction(AFT);
@@ -4569,6 +4584,7 @@
case CompletionKind::AfterPound: {
Lookup.addPoundAvailable(ParentStmtKind);
+ Lookup.addPoundSelector();
break;
}
}
diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp
index 73f75e4..70ab7e7 100644
--- a/lib/IDE/SyntaxModel.cpp
+++ b/lib/IDE/SyntaxModel.cpp
@@ -98,6 +98,7 @@
#define KEYWORD(X) case tok::kw_##X: Kind = SyntaxNodeKind::Keyword; break;
#include "swift/Parse/Tokens.def"
#undef KEYWORD
+ case tok::pound_selector: Kind = SyntaxNodeKind::Keyword; break;
case tok::pound_line:
case tok::pound_available: Kind =
SyntaxNodeKind::BuildConfigKeyword; break;
diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp
index 8807914..3e8c646 100644
--- a/lib/IRGen/GenDecl.cpp
+++ b/lib/IRGen/GenDecl.cpp
@@ -456,6 +456,30 @@
this->addLinkLibrary(LinkLibrary("objc", LibraryKind::Library));
}
+/// Collect elements of an already-existing global list with the given
+/// \c name into \c list.
+///
+/// We use this when Clang code generation might populate the list.
+static void collectGlobalList(IRGenModule &IGM,
+ SmallVectorImpl<llvm::WeakVH> &list,
+ StringRef name) {
+ if (auto *existing = IGM.Module.getGlobalVariable(name)) {
+ auto *globals = cast<llvm::ConstantArray>(existing->getInitializer());
+ for (auto &use : globals->operands()) {
+ auto *global = use.get();
+ list.push_back(global);
+ }
+ existing->eraseFromParent();
+ }
+
+ std::for_each(list.begin(), list.end(),
+ [](const llvm::WeakVH &global) {
+ assert(!isa<llvm::GlobalValue>(global) ||
+ !cast<llvm::GlobalValue>(global)->isDeclaration() &&
+ "all globals in the 'used' list must be definitions");
+ });
+}
+
/// Emit a global list, i.e. a global constant array holding all of a
/// list of values. Generally these lists are for various LLVM
/// metadata or runtime purposes.
@@ -630,6 +654,13 @@
LLVMUsed.push_back(global);
}
+/// Add the given global value to @llvm.compiler.used.
+///
+/// This value must have a definition by the time the module is finalized.
+void IRGenModule::addCompilerUsedGlobal(llvm::GlobalValue *global) {
+ LLVMCompilerUsed.push_back(global);
+}
+
/// Add the given global value to the Objective-C class list.
void IRGenModule::addObjCClass(llvm::Constant *classPtr, bool nonlazy) {
ObjCClasses.push_back(classPtr);
@@ -674,27 +705,19 @@
// @llvm.used
// Collect llvm.used globals already in the module (coming from ClangCodeGen).
- auto *ExistingLLVMUsed = Module.getGlobalVariable("llvm.used");
- if (ExistingLLVMUsed) {
- auto *Globals =
- cast<llvm::ConstantArray>(ExistingLLVMUsed->getInitializer());
- for (auto &Use : Globals->operands()) {
- auto *Global = Use.get();
- LLVMUsed.push_back(Global);
- }
- ExistingLLVMUsed->eraseFromParent();
- }
-
- std::for_each(LLVMUsed.begin(), LLVMUsed.end(),
- [](const llvm::WeakVH &global) {
- assert(!isa<llvm::GlobalValue>(global) ||
- !cast<llvm::GlobalValue>(global)->isDeclaration() &&
- "all globals in the 'used' list must be definitions");
- });
+ collectGlobalList(*this, LLVMUsed, "llvm.used");
emitGlobalList(*this, LLVMUsed, "llvm.used", "llvm.metadata",
llvm::GlobalValue::AppendingLinkage,
Int8PtrTy,
false);
+
+ // Collect llvm.compiler.used globals already in the module (coming
+ // from ClangCodeGen).
+ collectGlobalList(*this, LLVMCompilerUsed, "llvm.compiler.used");
+ emitGlobalList(*this, LLVMCompilerUsed, "llvm.compiler.used", "llvm.metadata",
+ llvm::GlobalValue::AppendingLinkage,
+ Int8PtrTy,
+ false);
}
void IRGenModuleDispatcher::emitGlobalTopLevel() {
diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp
index 1ff9b27..f494e93 100644
--- a/lib/IRGen/GenObjC.cpp
+++ b/lib/IRGen/GenObjC.cpp
@@ -292,12 +292,13 @@
// If not, create it. This implicitly adds a trailing null.
auto init = llvm::ConstantDataArray::getString(LLVMContext, selector);
- auto global = new llvm::GlobalVariable(Module, init->getType(), true,
- llvm::GlobalValue::InternalLinkage,
+ auto global = new llvm::GlobalVariable(Module, init->getType(), false,
+ llvm::GlobalValue::PrivateLinkage,
init,
llvm::Twine("\01L_selector_data(") + selector + ")");
global->setSection("__TEXT,__objc_methname,cstring_literals");
global->setAlignment(1);
+ addCompilerUsedGlobal(global);
// Drill down to make an i8*.
auto zero = llvm::ConstantInt::get(SizeTy, 0);
@@ -323,16 +324,17 @@
// choose something descriptive to make the IR readable.
auto init = getAddrOfObjCMethodName(selector);
auto global = new llvm::GlobalVariable(Module, init->getType(), false,
- llvm::GlobalValue::InternalLinkage,
+ llvm::GlobalValue::PrivateLinkage,
init,
llvm::Twine("\01L_selector(") + selector + ")");
+ global->setExternallyInitialized(true);
global->setAlignment(getPointerAlignment().getValue());
// This section name is magical for the Darwin static and dynamic linkers.
global->setSection("__DATA,__objc_selrefs,literal_pointers,no_dead_strip");
// Make sure that this reference does not get optimized away.
- addUsedGlobal(global);
+ addCompilerUsedGlobal(global);
// Cache and return.
entry = global;
diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h
index df93401..d0c164e 100644
--- a/lib/IRGen/IRGenModule.h
+++ b/lib/IRGen/IRGenModule.h
@@ -542,6 +542,7 @@
llvm::Constant *getAddrOfObjCProtocolRef(ProtocolDecl *proto,
ForDefinition_t forDefinition);
void addUsedGlobal(llvm::GlobalValue *global);
+ void addCompilerUsedGlobal(llvm::GlobalValue *global);
void addObjCClass(llvm::Constant *addr, bool nonlazy);
void addProtocolConformanceRecord(NormalProtocolConformance *conformance);
@@ -571,6 +572,14 @@
/// out.
SmallVector<llvm::WeakVH, 4> LLVMUsed;
+ /// LLVMCompilerUsed - List of global values which are required to be
+ /// present in the object file; bitcast to i8*. This is used for
+ /// forcing visibility of symbols which may otherwise be optimized
+ /// out.
+ ///
+ /// Similar to LLVMUsed, but emitted as llvm.compiler.used.
+ SmallVector<llvm::WeakVH, 4> LLVMCompilerUsed;
+
/// Metadata nodes for autolinking info.
///
/// This is typed using llvm::Value instead of llvm::MDNode because it
diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp
index d066fbe..4017692 100644
--- a/lib/IRGen/IRGenSIL.cpp
+++ b/lib/IRGen/IRGenSIL.cpp
@@ -2236,18 +2236,28 @@
case swift::StringLiteralInst::Encoding::UTF8:
return IGM.getAddrOfGlobalString(string);
- case swift::StringLiteralInst::Encoding::UTF16:
+ case swift::StringLiteralInst::Encoding::UTF16: {
// This is always a GEP of a GlobalVariable with a nul terminator.
auto addr = IGM.getAddrOfGlobalUTF16String(string);
// Cast to Builtin.RawPointer.
return llvm::ConstantExpr::getBitCast(addr, IGM.Int8PtrTy);
}
+
+ case swift::StringLiteralInst::Encoding::ObjCSelector:
+ llvm_unreachable("cannot get the address of an Objective-C selector");
+ }
llvm_unreachable("bad string encoding");
}
void IRGenSILFunction::visitStringLiteralInst(swift::StringLiteralInst *i) {
- auto addr = getAddrOfString(IGM, i->getValue(), i->getEncoding());
+ llvm::Value *addr;
+
+ // Emit a load of a selector.
+ if (i->getEncoding() == swift::StringLiteralInst::Encoding::ObjCSelector)
+ addr = emitObjCSelectorRefLoad(i->getValue());
+ else
+ addr = getAddrOfString(IGM, i->getValue(), i->getEncoding());
Explosion e;
e.add(addr);
diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp
index dc4c2ab..fdd5ca1 100644
--- a/lib/Parse/Lexer.cpp
+++ b/lib/Parse/Lexer.cpp
@@ -1611,6 +1611,11 @@
return formToken(tok::pound_available, TokStart);
}
+ if (getSubstring(TokStart + 1, 8).equals("selector")) {
+ CurPtr += 8;
+ return formToken(tok::pound_selector, TokStart);
+ }
+
// Allow a hashbang #! line at the beginning of the file.
if (CurPtr - 1 == BufferStart && *CurPtr == '!') {
CurPtr--;
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index cab8211..5ae1eef 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -417,6 +417,7 @@
/// expr-postfix(Mode)
/// operator-prefix expr-unary(Mode)
/// '&' expr-unary(Mode)
+/// expr-selector
///
ParserResult<Expr> Parser::parseExprUnary(Diag<> Message, bool isExprBasic) {
UnresolvedDeclRefExpr *Operator;
@@ -437,6 +438,9 @@
new (Context) InOutExpr(Loc, SubExpr.get(), Type()));
}
+ case tok::pound_selector:
+ return parseExprSelector();
+
case tok::oper_postfix:
// Postfix operators cannot start a subexpression, but can happen
// syntactically because the operator may just follow whatever precedes this
@@ -482,6 +486,50 @@
new (Context) PrefixUnaryExpr(Operator, SubExpr.get()));
}
+/// parseExprSelector
+///
+/// expr-selector:
+/// '#selector' '(' expr ')'
+///
+ParserResult<Expr> Parser::parseExprSelector() {
+ // Consume '#selector'.
+ SourceLoc keywordLoc = consumeToken(tok::pound_selector);
+
+ // Parse the leading '('.
+ if (!Tok.is(tok::l_paren)) {
+ diagnose(Tok, diag::expr_selector_expected_lparen);
+ return makeParserError();
+ }
+ SourceLoc lParenLoc = consumeToken(tok::l_paren);
+
+ // Parse the subexpression.
+ ParserResult<Expr> subExpr = parseExpr(diag::expr_selector_expected_expr);
+ if (subExpr.hasCodeCompletion())
+ return subExpr;
+
+ // Parse the closing ')'
+ SourceLoc rParenLoc;
+ if (subExpr.isParseError()) {
+ skipUntilDeclStmtRBrace(tok::r_paren);
+ if (Tok.is(tok::r_paren))
+ rParenLoc = consumeToken();
+ else
+ rParenLoc = Tok.getLoc();
+ } else {
+ parseMatchingToken(tok::r_paren, rParenLoc,
+ diag::expr_selector_expected_rparen, lParenLoc);
+ }
+
+ // If the subexpression was in error, just propagate the error.
+ if (subExpr.isParseError())
+ return makeParserResult<Expr>(
+ new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc)));
+
+ return makeParserResult<Expr>(
+ new (Context) ObjCSelectorExpr(keywordLoc, lParenLoc, subExpr.get(),
+ rParenLoc));
+}
+
static DeclRefKind getDeclRefKindForOperator(tok kind) {
switch (kind) {
case tok::oper_binary_spaced:
diff --git a/lib/Parse/ParseSIL.cpp b/lib/Parse/ParseSIL.cpp
index de946f2..ed8aa44 100644
--- a/lib/Parse/ParseSIL.cpp
+++ b/lib/Parse/ParseSIL.cpp
@@ -1772,6 +1772,8 @@
encoding = StringLiteralInst::Encoding::UTF8;
} else if (P.Tok.getText() == "utf16") {
encoding = StringLiteralInst::Encoding::UTF16;
+ } else if (P.Tok.getText() == "objc_selector") {
+ encoding = StringLiteralInst::Encoding::ObjCSelector;
} else {
P.diagnose(P.Tok, diag::sil_string_invalid_encoding, P.Tok.getText());
return true;
diff --git a/lib/SIL/SILGlobalVariable.cpp b/lib/SIL/SILGlobalVariable.cpp
index 773de01..d72be17 100644
--- a/lib/SIL/SILGlobalVariable.cpp
+++ b/lib/SIL/SILGlobalVariable.cpp
@@ -111,12 +111,24 @@
}
}
+ // Objective-C selector string literals cannot be used in static
+ // initializers.
+ if (auto *stringLit = dyn_cast<StringLiteralInst>(&I)) {
+ switch (stringLit->getEncoding()) {
+ case StringLiteralInst::Encoding::UTF8:
+ case StringLiteralInst::Encoding::UTF16:
+ continue;
+
+ case StringLiteralInst::Encoding::ObjCSelector:
+ return false;
+ }
+ }
+
if (I.getKind() != ValueKind::ReturnInst &&
I.getKind() != ValueKind::StructInst &&
I.getKind() != ValueKind::TupleInst &&
I.getKind() != ValueKind::IntegerLiteralInst &&
- I.getKind() != ValueKind::FloatLiteralInst &&
- I.getKind() != ValueKind::StringLiteralInst)
+ I.getKind() != ValueKind::FloatLiteralInst)
return false;
}
}
diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp
index 7c25075..cfc5127 100644
--- a/lib/SIL/SILPrinter.cpp
+++ b/lib/SIL/SILPrinter.cpp
@@ -856,6 +856,7 @@
switch (kind) {
case StringLiteralInst::Encoding::UTF8: return "utf8 ";
case StringLiteralInst::Encoding::UTF16: return "utf16 ";
+ case StringLiteralInst::Encoding::ObjCSelector: return "objc_selector ";
}
llvm_unreachable("bad string literal encoding");
}
diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp
index 8d34399..9864cb92 100644
--- a/lib/SILGen/SILGenExpr.cpp
+++ b/lib/SILGen/SILGenExpr.cpp
@@ -197,6 +197,7 @@
SGFContext C);
RValue visitObjectLiteralExpr(ObjectLiteralExpr *E, SGFContext C);
RValue visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, SGFContext C);
+ RValue visitObjCSelectorExpr(ObjCSelectorExpr *E, SGFContext C);
RValue visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E,
SGFContext C);
RValue visitCollectionExpr(CollectionExpr *E, SGFContext C);
@@ -822,6 +823,9 @@
case StringLiteralInst::Encoding::UTF8:
Elts = EltsArray;
break;
+
+ case StringLiteralInst::Encoding::ObjCSelector:
+ llvm_unreachable("Objective-C selectors cannot be formed here");
}
return RValue(Elts, ty);
@@ -1957,6 +1961,46 @@
return visit(E->getSemanticExpr(), C);
}
+RValue RValueEmitter::visitObjCSelectorExpr(ObjCSelectorExpr *e, SGFContext C) {
+ SILType loweredSelectorTy = SGF.getLoweredType(e->getType());
+
+ // Dig out the declaration of the Selector type.
+ auto selectorDecl = e->getType()->getAs<StructType>()->getDecl();
+
+ // Dig out the type of its pointer.
+ Type selectorMemberTy;
+ for (auto member : selectorDecl->getMembers()) {
+ if (auto var = dyn_cast<VarDecl>(member)) {
+ if (!var->isStatic() && var->hasStorage()) {
+ selectorMemberTy = var->getInterfaceType()->getRValueType();
+ break;
+ }
+ }
+ }
+ if (!selectorMemberTy) {
+ SGF.SGM.diagnose(e, diag::objc_selector_malformed);
+ return RValue(SGF, e, SGF.emitUndef(e, loweredSelectorTy));
+ }
+
+ // Form the selector string.
+ llvm::SmallString<64> selectorScratch;
+ auto selectorString =
+ e->getMethod()->getObjCSelector().getString(selectorScratch);
+
+ // Create an Objective-C selector string literal.
+ auto selectorLiteral =
+ SGF.B.createStringLiteral(e, selectorString,
+ StringLiteralInst::Encoding::ObjCSelector);
+
+ // Create the pointer struct from the raw pointer.
+ SILType loweredPtrTy = SGF.getLoweredType(selectorMemberTy);
+ auto ptrValue = SGF.B.createStruct(e, loweredPtrTy, { selectorLiteral });
+
+ // Wrap that up in a Selector and return it.
+ auto selectorValue = SGF.B.createStruct(e, loweredSelectorTy, { ptrValue });
+ return RValue(SGF, e, ManagedValue::forUnmanaged(selectorValue));
+}
+
static StringRef
getMagicFunctionString(SILGenFunction &gen) {
assert(gen.MagicFunctionName
diff --git a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
index e33cf15..dd679ad 100644
--- a/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
+++ b/lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp
@@ -216,6 +216,12 @@
if (CD->isFinal())
return true;
+ // If the class has an @objc ancestry it can be dynamically subclassed and we
+ // can't therefore statically know the default case.
+ auto Ancestry = CD->checkObjCAncestry();
+ if (Ancestry != ObjCClassKind::NonObjC)
+ return false;
+
// Without an associated context we cannot perform any
// access-based optimizations.
if (!DC)
diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp
index 86e38c8..159c29c 100644
--- a/lib/Sema/CSApply.cpp
+++ b/lib/Sema/CSApply.cpp
@@ -3262,6 +3262,147 @@
return E;
}
+ Expr *visitObjCSelectorExpr(ObjCSelectorExpr *E) {
+ // Dig out the reference to a declaration.
+ Expr *subExpr = E->getSubExpr();
+ ValueDecl *foundDecl = nullptr;
+ while (subExpr) {
+ // Declaration reference.
+ if (auto declRef = dyn_cast<DeclRefExpr>(subExpr)) {
+ foundDecl = declRef->getDecl();
+ break;
+ }
+
+ // Constructor reference.
+ if (auto ctorRef = dyn_cast<OtherConstructorDeclRefExpr>(subExpr)) {
+ foundDecl = ctorRef->getDecl();
+ break;
+ }
+
+ // Member reference.
+ if (auto memberRef = dyn_cast<MemberRefExpr>(subExpr)) {
+ foundDecl = memberRef->getMember().getDecl();
+ break;
+ }
+
+ // Dynamic member reference.
+ if (auto dynMemberRef = dyn_cast<DynamicMemberRefExpr>(subExpr)) {
+ foundDecl = dynMemberRef->getMember().getDecl();
+ break;
+ }
+
+ // Look through parentheses.
+ if (auto paren = dyn_cast<ParenExpr>(subExpr)) {
+ subExpr = paren->getSubExpr();
+ continue;
+ }
+
+ // Look through "a.b" to "b".
+ if (auto dotSyntax = dyn_cast<DotSyntaxBaseIgnoredExpr>(subExpr)) {
+ subExpr = dotSyntax->getRHS();
+ continue;
+ }
+
+ // Look through self-rebind expression.
+ if (auto rebindSelf = dyn_cast<RebindSelfInConstructorExpr>(subExpr)) {
+ subExpr = rebindSelf->getSubExpr();
+ continue;
+ }
+
+ // Look through optional binding within the monadic "?".
+ if (auto bind = dyn_cast<BindOptionalExpr>(subExpr)) {
+ subExpr = bind->getSubExpr();
+ continue;
+ }
+
+ // Look through optional evaluation of the monadic "?".
+ if (auto optEval = dyn_cast<OptionalEvaluationExpr>(subExpr)) {
+ subExpr = optEval->getSubExpr();
+ continue;
+ }
+
+ // Look through an implicit force-value.
+ if (auto force = dyn_cast<ForceValueExpr>(subExpr)) {
+ subExpr = force->getSubExpr();
+ continue;
+ }
+
+ // Look through implicit open-existential operations.
+ if (auto open = dyn_cast<OpenExistentialExpr>(subExpr)) {
+ if (open->isImplicit()) {
+ subExpr = open->getSubExpr();
+ continue;
+ }
+ break;
+ }
+
+ // Look to the referenced member in a self-application.
+ if (auto selfApply = dyn_cast<SelfApplyExpr>(subExpr)) {
+ subExpr = selfApply->getFn();
+ continue;
+ }
+
+ // Look through implicit conversions.
+ if (auto conversion = dyn_cast<ImplicitConversionExpr>(subExpr)) {
+ subExpr = conversion->getSubExpr();
+ continue;
+ }
+
+ // Look through explicit coercions.
+ if (auto coercion = dyn_cast<CoerceExpr>(subExpr)) {
+ subExpr = coercion->getSubExpr();
+ continue;
+ }
+
+ break;
+ }
+
+ if (!subExpr) return nullptr;
+
+ // If we didn't find any declaration at all, we're stuck.
+ auto &tc = cs.getTypeChecker();
+ if (!foundDecl) {
+ tc.diagnose(E->getLoc(), diag::expr_selector_no_declaration)
+ .highlight(subExpr->getSourceRange());
+ return E;
+ }
+
+ // If the declaration we found was not a method or initializer,
+ // complain.
+ auto func = dyn_cast<AbstractFunctionDecl>(foundDecl);
+ if (!func) {
+ tc.diagnose(E->getLoc(),
+ isa<VarDecl>(foundDecl)
+ ? diag::expr_selector_property
+ : diag::expr_selector_not_method_or_init)
+ .highlight(subExpr->getSourceRange());
+ tc.diagnose(foundDecl, diag::decl_declared_here,
+ foundDecl->getFullName());
+ return E;
+ }
+
+ // The declaration we found must be exposed to Objective-C.
+ if (!func->isObjC()) {
+ tc.diagnose(E->getLoc(), diag::expr_selector_not_objc,
+ isa<ConstructorDecl>(func))
+ .highlight(subExpr->getSourceRange());
+ if (foundDecl->getLoc().isValid()) {
+ tc.diagnose(foundDecl,
+ diag::expr_selector_make_objc,
+ isa<ConstructorDecl>(func))
+ .fixItInsert(foundDecl->getAttributeInsertionLoc(false),
+ "@objc ");
+ } else {
+ tc.diagnose(foundDecl, diag::decl_declared_here,
+ foundDecl->getFullName());
+ }
+ return E;
+ }
+
+ E->setMethod(func);
+ return E;
+ }
+
/// Interface for ExprWalker
void walkToExprPre(Expr *expr) {
ExprStack.push_back(expr);
diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp
index d99e3c0..a962e58 100644
--- a/lib/Sema/CSGen.cpp
+++ b/lib/Sema/CSGen.cpp
@@ -2479,6 +2479,26 @@
// case we return the null type.
return E->getType();
}
+
+ Type visitObjCSelectorExpr(ObjCSelectorExpr *E) {
+ // #selector only makes sense when we have the Objective-C
+ // runtime.
+ auto &tc = CS.getTypeChecker();
+ if (!tc.Context.LangOpts.EnableObjCInterop) {
+ tc.diagnose(E->getLoc(), diag::expr_selector_no_objc_runtime);
+ return nullptr;
+ }
+
+ // Make sure we can reference ObjectiveC.Selector.
+ // FIXME: Fix-It to add the import?
+ auto type = CS.getTypeChecker().getObjCSelectorType(CS.DC);
+ if (!type) {
+ tc.diagnose(E->getLoc(), diag::expr_selector_module_missing);
+ return nullptr;
+ }
+
+ return type;
+ }
};
/// \brief AST walker that "sanitizes" an expression for the
diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp
index 203ad72..7ad9814 100644
--- a/lib/Sema/MiscDiagnostics.cpp
+++ b/lib/Sema/MiscDiagnostics.cpp
@@ -1825,6 +1825,365 @@
.fixItRemoveChars(FS->getSecondSemicolonLoc(), endOfIncrementLoc);
}
+static Optional<ObjCSelector>
+parseObjCSelector(ASTContext &ctx, StringRef string) {
+ // Find the first colon.
+ auto colonPos = string.find(':');
+
+ // If there is no colon, we have a nullary selector.
+ if (colonPos == StringRef::npos) {
+ if (string.empty() || !Lexer::isIdentifier(string)) return None;
+ return ObjCSelector(ctx, 0, { ctx.getIdentifier(string) });
+ }
+
+ SmallVector<Identifier, 2> pieces;
+ do {
+ // Check whether we have a valid selector piece.
+ auto piece = string.substr(0, colonPos);
+ if (piece.empty()) {
+ pieces.push_back(Identifier());
+ } else {
+ if (!Lexer::isIdentifier(piece)) return None;
+ pieces.push_back(ctx.getIdentifier(piece));
+ }
+
+ // Move to the next piece.
+ string = string.substr(colonPos+1);
+ colonPos = string.find(':');
+ } while (colonPos != StringRef::npos);
+
+ // If anything remains of the string, it's not a selector.
+ if (!string.empty()) return None;
+
+ return ObjCSelector(ctx, pieces.size(), pieces);
+}
+
+
+namespace {
+
+class ObjCSelectorWalker : public ASTWalker {
+ TypeChecker &TC;
+ const DeclContext *DC;
+ Type SelectorTy;
+
+ /// Determine whether a reference to the given method via its
+ /// enclosing class/protocol is ambiguous (and, therefore, needs to
+ /// be disambiguated with a coercion).
+ bool isSelectorReferenceAmbiguous(AbstractFunctionDecl *method) {
+ // Determine the name we would search for. If there are no
+ // argument names, our lookup will be based solely on the base
+ // name.
+ DeclName lookupName = method->getFullName();
+ if (lookupName.getArgumentNames().empty())
+ lookupName = lookupName.getBaseName();
+
+ // Look for members with the given name.
+ auto nominal =
+ method->getDeclContext()->isNominalTypeOrNominalTypeExtensionContext();
+ auto result = TC.lookupMember(const_cast<DeclContext *>(DC),
+ nominal->getInterfaceType(), lookupName,
+ (defaultMemberLookupOptions |
+ NameLookupFlags::KnownPrivate));
+
+ // If we didn't find multiple methods, there is no ambiguity.
+ if (result.size() < 2) return false;
+
+ // If we found more than two methods, it's ambiguous.
+ if (result.size() > 2) return true;
+
+ // Dig out the methods.
+ auto firstMethod = dyn_cast<FuncDecl>(result[0].Decl);
+ auto secondMethod = dyn_cast<FuncDecl>(result[1].Decl);
+ if (!firstMethod || !secondMethod) return true;
+
+ // If one is a static/class method and the other is not...
+ if (firstMethod->isStatic() == secondMethod->isStatic()) return true;
+
+ // ... overload resolution will prefer the static method. Check
+ // that it has the correct selector. We don't even care that it's
+ // the same method we're asking for, just that it has the right
+ // selector.
+ FuncDecl *staticMethod =
+ firstMethod->isStatic() ? firstMethod : secondMethod;
+ return staticMethod->getObjCSelector() != method->getObjCSelector();
+ }
+
+public:
+ ObjCSelectorWalker(TypeChecker &tc, const DeclContext *dc, Type selectorTy)
+ : TC(tc), DC(dc), SelectorTy(selectorTy) { }
+
+ virtual std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
+ // Only diagnose calls.
+ auto call = dyn_cast<CallExpr>(expr);
+ if (!call) return { true, expr };
+
+ // That produce Selectors.
+ if (!call->getType() || !call->getType()->isEqual(SelectorTy))
+ return { true, expr };
+
+ // Via a constructor.
+ ConstructorDecl *ctor = nullptr;
+ if (auto ctorRefCall = dyn_cast<ConstructorRefCallExpr>(call->getFn())) {
+ if (auto ctorRef = dyn_cast<DeclRefExpr>(ctorRefCall->getFn()))
+ ctor = dyn_cast<ConstructorDecl>(ctorRef->getDecl());
+ else if (auto otherCtorRef =
+ dyn_cast<OtherConstructorDeclRefExpr>(ctorRefCall->getFn()))
+ ctor = otherCtorRef->getDecl();
+ }
+
+ if (!ctor) return { true, expr };
+
+ // Make sure the constructor is within Selector.
+ auto ctorContextType = ctor->getDeclContext()->getDeclaredTypeOfContext();
+ if (!ctorContextType || !ctorContextType->isEqual(SelectorTy))
+ return { true, expr };
+
+ auto argNames = ctor->getFullName().getArgumentNames();
+ if (argNames.size() != 1) return { true, expr };
+
+ // Is this the init(stringLiteral:) initializer or init(_:) initializer?
+ bool fromStringLiteral = false;
+ if (argNames[0] == TC.Context.Id_stringLiteral)
+ fromStringLiteral = true;
+ else if (!argNames[0].empty())
+ return { true, expr };
+
+ // Dig out the argument.
+ Expr *arg = call->getArg()->getSemanticsProvidingExpr();
+ if (auto tupleExpr = dyn_cast<TupleExpr>(arg)) {
+ if (tupleExpr->getNumElements() == 1 &&
+ tupleExpr->getElementName(0) == TC.Context.Id_stringLiteral)
+ arg = tupleExpr->getElement(0)->getSemanticsProvidingExpr();
+ }
+
+ // If the argument is a call, it might be to
+ // init(__builtinStringLiteral:byteSize:isASCII:). If so, dig
+ // through that.
+ if (auto argCall = dyn_cast<CallExpr>(arg)) {
+ if (auto ctorRefCall =
+ dyn_cast<ConstructorRefCallExpr>(argCall->getFn())) {
+ if (auto argCtor =
+ dyn_cast_or_null<ConstructorDecl>(ctorRefCall->getCalledValue())){
+ auto argArgumentNames = argCtor->getFullName().getArgumentNames();
+ if (argArgumentNames.size() == 3 &&
+ argArgumentNames[0] == TC.Context.Id_builtinStringLiteral) {
+ arg = argCall->getArg()->getSemanticsProvidingExpr();
+ }
+ }
+ }
+ }
+
+ // Check whether we have a string literal.
+ auto stringLiteral = dyn_cast<StringLiteralExpr>(arg);
+ if (!stringLiteral) return { true, expr };
+
+ /// Retrieve the parent expression that coerces to Selector, if
+ /// there is one.
+ auto getParentCoercion = [&]() -> CoerceExpr * {
+ auto parentExpr = Parent.getAsExpr();
+ if (!parentExpr) return nullptr;
+
+ auto coerce = dyn_cast<CoerceExpr>(parentExpr);
+ if (!coerce) return nullptr;
+
+ if (coerce->getType() && coerce->getType()->isEqual(SelectorTy))
+ return coerce;
+
+ return nullptr;
+ };
+
+ // Local function that adds the constructor syntax around string
+ // literals implicitly treated as a Selector.
+ auto addSelectorConstruction = [&](InFlightDiagnostic &diag) {
+ if (!fromStringLiteral) return;
+
+ // Introduce the beginning part of the Selector construction.
+ diag.fixItInsert(stringLiteral->getLoc(), "Selector(");
+
+ if (auto coerce = getParentCoercion()) {
+ // If the string literal was coerced to Selector, replace the
+ // coercion with the ")".
+ SourceLoc endLoc = Lexer::getLocForEndOfToken(TC.Context.SourceMgr,
+ expr->getEndLoc());
+ diag.fixItReplace(SourceRange(endLoc, coerce->getEndLoc()), ")");
+ } else {
+ // Otherwise, just insert the closing ")".
+ diag.fixItInsertAfter(stringLiteral->getEndLoc(), ")");
+ }
+ };
+
+ // Try to parse the string literal as an Objective-C selector, and complain
+ // if it isn't one.
+ auto selector = parseObjCSelector(TC.Context, stringLiteral->getValue());
+ if (!selector) {
+ auto diag = TC.diagnose(stringLiteral->getLoc(),
+ diag::selector_literal_invalid);
+ diag.highlight(stringLiteral->getSourceRange());
+ addSelectorConstruction(diag);
+ return { true, expr };
+ }
+
+ // Look for methods with this selector.
+ SmallVector<AbstractFunctionDecl *, 8> allMethods;
+ DC->lookupAllObjCMethods(*selector, allMethods);
+
+ // If we didn't find any methods, complain.
+ if (allMethods.empty()) {
+ auto diag =
+ TC.diagnose(stringLiteral->getLoc(), diag::selector_literal_undeclared,
+ *selector);
+ addSelectorConstruction(diag);
+ return { true, expr };
+ }
+
+ // Separate out the accessor methods.
+ auto splitPoint =
+ std::stable_partition(allMethods.begin(), allMethods.end(),
+ [](AbstractFunctionDecl *abstractFunc) -> bool {
+ if (auto func = dyn_cast<FuncDecl>(abstractFunc))
+ return !func->isAccessor();
+
+ return true;
+ });
+ ArrayRef<AbstractFunctionDecl *> methods(allMethods.begin(), splitPoint);
+ ArrayRef<AbstractFunctionDecl *> accessors(splitPoint, allMethods.end());
+
+ // Find the "best" method that has this selector, so we can report
+ // that.
+ AbstractFunctionDecl *bestMethod = nullptr;
+ for (auto method : methods) {
+ // If this is the first method, use it.
+ if (!bestMethod) {
+ bestMethod = method;
+ continue;
+ }
+
+ // If referencing the best method would produce an ambiguity and
+ // referencing the new method would not, we have a new "best".
+ if (isSelectorReferenceAmbiguous(bestMethod) &&
+ !isSelectorReferenceAmbiguous(method)) {
+ bestMethod = method;
+ continue;
+ }
+
+ // If this method is within a protocol...
+ if (auto proto =
+ method->getDeclContext()->isProtocolOrProtocolExtensionContext()) {
+ // If the best so far is not from a protocol, or is from a
+ // protocol that inherits this protocol, we have a new best.
+ auto bestProto = bestMethod->getDeclContext()
+ ->isProtocolOrProtocolExtensionContext();
+ if (!bestProto || bestProto->inheritsFrom(proto))
+ bestMethod = method;
+ continue;
+ }
+
+ // This method is from a class.
+ auto classDecl =
+ method->getDeclContext()->isClassOrClassExtensionContext();
+
+ // If the best method was from a protocol, keep it.
+ auto bestClassDecl =
+ bestMethod->getDeclContext()->isClassOrClassExtensionContext();
+ if (!bestClassDecl) continue;
+
+ // If the best method was from a subclass of the place where
+ // this method was declared, we have a new best.
+ while (auto superclassTy = bestClassDecl->getSuperclass()) {
+ auto superclassDecl = superclassTy->getClassOrBoundGenericClass();
+ if (!superclassDecl) break;
+
+ if (classDecl == superclassDecl) {
+ bestMethod = method;
+ break;
+ }
+
+ bestClassDecl = superclassDecl;
+ }
+ }
+
+ // If we have a best method, reference it.
+ if (bestMethod) {
+ // Form the replacement #selector expression.
+ SmallString<32> replacement;
+ {
+ llvm::raw_svector_ostream out(replacement);
+ auto nominal = bestMethod->getDeclContext()
+ ->isNominalTypeOrNominalTypeExtensionContext();
+ auto name = bestMethod->getFullName();
+ out << "#selector(" << nominal->getName().str() << "."
+ << name.getBaseName().str();
+ auto argNames = name.getArgumentNames();
+
+ // Only print the parentheses if there are some argument
+ // names, because "()" would indicate a call.
+ if (argNames.size() > 0) {
+ out << "(";
+ for (auto argName : argNames) {
+ if (argName.empty()) out << "_";
+ else out << argName.str();
+ out << ":";
+ }
+ out << ")";
+ }
+
+ // If there will be an ambiguity when referring to the method,
+ // introduce a coercion to resolve it to the method we found.
+ if (isSelectorReferenceAmbiguous(bestMethod)) {
+ if (auto fnType =
+ bestMethod->getInterfaceType()->getAs<FunctionType>()) {
+ // For static/class members, drop the metatype argument.
+ if (bestMethod->isStatic())
+ fnType = fnType->getResult()->getAs<FunctionType>();
+
+ // Drop the argument labels.
+ // FIXME: They never should have been in the type anyway.
+ Type type = fnType->getUnlabeledType(TC.Context);
+
+ // Coerce to this type.
+ out << " as ";
+ type.print(out);
+ }
+ }
+
+ out << ")";
+ }
+
+ // Emit the diagnostic.
+ SourceRange replacementRange = expr->getSourceRange();
+ if (auto coerce = getParentCoercion())
+ replacementRange.End = coerce->getEndLoc();
+
+ TC.diagnose(expr->getLoc(),
+ fromStringLiteral ? diag::selector_literal_deprecated_suggest
+ : diag::selector_construction_suggest)
+ .fixItReplace(replacementRange, replacement);
+ return { true, expr };
+ }
+
+ // If we couldn't pick a method to use for #selector, just wrap
+ // the string literal in Selector(...).
+ if (fromStringLiteral) {
+ auto diag = TC.diagnose(stringLiteral->getLoc(),
+ diag::selector_literal_deprecated);
+ addSelectorConstruction(diag);
+ return { true, expr };
+ }
+
+ return { true, expr };
+ }
+
+};
+}
+
+static void diagDeprecatedObjCSelectors(TypeChecker &tc, const DeclContext *dc,
+ const Expr *expr) {
+ auto selectorTy = tc.getObjCSelectorType(const_cast<DeclContext *>(dc));
+ if (!selectorTy) return;
+
+ const_cast<Expr *>(expr)->walk(ObjCSelectorWalker(tc, dc, selectorTy));
+}
+
//===----------------------------------------------------------------------===//
// High-level entry points.
//===----------------------------------------------------------------------===//
@@ -1838,6 +2197,8 @@
diagRecursivePropertyAccess(TC, E, DC);
diagnoseImplicitSelfUseInClosure(TC, E, DC);
diagAvailability(TC, E, const_cast<DeclContext*>(DC));
+ if (TC.Context.LangOpts.EnableObjCInterop)
+ diagDeprecatedObjCSelectors(TC, DC, E);
}
void swift::performStmtDiagnostics(TypeChecker &TC, const Stmt *S) {
diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp
index cbffef8..1f6d04e 100644
--- a/lib/Sema/TypeCheckDecl.cpp
+++ b/lib/Sema/TypeCheckDecl.cpp
@@ -2387,6 +2387,14 @@
method->setForeignErrorConvention(*errorConvention);
}
}
+
+ // Record this method in the source-file-specific Objective-C method
+ // table.
+ if (auto method = dyn_cast<AbstractFunctionDecl>(D)) {
+ if (auto sourceFile = method->getParentSourceFile()) {
+ sourceFile->ObjCMethods[method->getObjCSelector()].push_back(method);
+ }
+ }
}
namespace {
diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp
index 1d8165e..6cc1459 100644
--- a/lib/Sema/TypeCheckType.cpp
+++ b/lib/Sema/TypeCheckType.cpp
@@ -110,11 +110,11 @@
return Type();
}
-static Type getObjectiveCClassType(TypeChecker &TC,
- Type &cache,
- Identifier ModuleName,
- Identifier TypeName,
- DeclContext *dc) {
+static Type getObjectiveCNominalType(TypeChecker &TC,
+ Type &cache,
+ Identifier ModuleName,
+ Identifier TypeName,
+ DeclContext *dc) {
if (cache)
return cache;
@@ -141,17 +141,24 @@
}
Type TypeChecker::getNSObjectType(DeclContext *dc) {
- return getObjectiveCClassType(*this, NSObjectType, Context.Id_ObjectiveC,
+ return getObjectiveCNominalType(*this, NSObjectType, Context.Id_ObjectiveC,
Context.getSwiftId(
KnownFoundationEntity::NSObject),
dc);
}
Type TypeChecker::getNSErrorType(DeclContext *dc) {
- return getObjectiveCClassType(*this, NSObjectType, Context.Id_Foundation,
- Context.getSwiftId(
- KnownFoundationEntity::NSError),
- dc);
+ return getObjectiveCNominalType(*this, NSObjectType, Context.Id_Foundation,
+ Context.getSwiftId(
+ KnownFoundationEntity::NSError),
+ dc);
+}
+
+Type TypeChecker::getObjCSelectorType(DeclContext *dc) {
+ return getObjectiveCNominalType(*this, ObjCSelectorType,
+ Context.Id_ObjectiveC,
+ Context.Id_Selector,
+ dc);
}
Type TypeChecker::getBridgedToObjC(const DeclContext *dc, Type type) {
diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h
index af8d983..aa0dafe 100644
--- a/lib/Sema/TypeChecker.h
+++ b/lib/Sema/TypeChecker.h
@@ -508,6 +508,7 @@
Type UInt8Type;
Type NSObjectType;
Type NSErrorType;
+ Type ObjCSelectorType;
Type ExceptionType;
/// The \c Swift.UnsafeMutablePointer<T> declaration.
@@ -582,6 +583,7 @@
Type getUInt8Type(DeclContext *dc);
Type getNSObjectType(DeclContext *dc);
Type getNSErrorType(DeclContext *dc);
+ Type getObjCSelectorType(DeclContext *dc);
Type getExceptionType(DeclContext *dc, SourceLoc loc);
/// \brief Try to resolve an IdentTypeRepr, returning either the referenced
diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp
index d66e4e2..1ed3c07 100644
--- a/lib/Serialization/DeserializeSIL.cpp
+++ b/lib/Serialization/DeserializeSIL.cpp
@@ -38,6 +38,7 @@
switch (value) {
case SIL_UTF8: return StringLiteralInst::Encoding::UTF8;
case SIL_UTF16: return StringLiteralInst::Encoding::UTF16;
+ case SIL_OBJC_SELECTOR: return StringLiteralInst::Encoding::ObjCSelector;
default: return None;
}
}
diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp
index f4cf604..f856cd8 100644
--- a/lib/Serialization/ModuleFile.cpp
+++ b/lib/Serialization/ModuleFile.cpp
@@ -1405,6 +1405,25 @@
}
}
+void ModuleFile::lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) {
+ // If we don't have an Objective-C method table, there's nothing to do.
+ if (!ObjCMethods) return;
+
+ // Look for all methods in the module file with this selector.
+ auto known = ObjCMethods->find(selector);
+ if (known == ObjCMethods->end()) return;
+
+ auto found = *known;
+ for (const auto &result : found) {
+ // Deserialize the method and add it to the list.
+ if (auto func = dyn_cast_or_null<AbstractFunctionDecl>(
+ getDecl(std::get<2>(result))))
+ results.push_back(func);
+ }
+}
+
void
ModuleFile::collectLinkLibraries(Module::LinkLibraryCallback callback) const {
for (auto &lib : LinkLibraries)
diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h
index 4cba484..7b3ec1d 100644
--- a/lib/Serialization/SILFormat.h
+++ b/lib/Serialization/SILFormat.h
@@ -32,7 +32,8 @@
enum SILStringEncoding : uint8_t {
SIL_UTF8,
- SIL_UTF16
+ SIL_UTF16,
+ SIL_OBJC_SELECTOR,
};
enum SILLinkageEncoding : uint8_t {
diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp
index 333d35d..ae0d561 100644
--- a/lib/Serialization/SerializeSIL.cpp
+++ b/lib/Serialization/SerializeSIL.cpp
@@ -37,6 +37,7 @@
switch (encoding) {
case StringLiteralInst::Encoding::UTF8: return SIL_UTF8;
case StringLiteralInst::Encoding::UTF16: return SIL_UTF16;
+ case StringLiteralInst::Encoding::ObjCSelector: return SIL_OBJC_SELECTOR;
}
llvm_unreachable("bad string encoding");
}
diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp
index 96a14b6..3e769fb 100644
--- a/lib/Serialization/SerializedModuleLoader.cpp
+++ b/lib/Serialization/SerializedModuleLoader.cpp
@@ -476,6 +476,12 @@
File.lookupClassMember(accessPath, name, decls);
}
+void SerializedASTFile::lookupObjCMethods(
+ ObjCSelector selector,
+ SmallVectorImpl<AbstractFunctionDecl *> &results) const {
+ File.lookupObjCMethods(selector, results);
+}
+
Optional<BriefAndRawComment>
SerializedASTFile::getCommentForDecl(const Decl *D) const {
return File.getCommentForDecl(D);
diff --git a/stdlib/private/StdlibUnittestFoundationExtras/StdlibUnittestFoundationExtras.swift b/stdlib/private/StdlibUnittestFoundationExtras/StdlibUnittestFoundationExtras.swift
index 5966470..2514722 100644
--- a/stdlib/private/StdlibUnittestFoundationExtras/StdlibUnittestFoundationExtras.swift
+++ b/stdlib/private/StdlibUnittestFoundationExtras/StdlibUnittestFoundationExtras.swift
@@ -27,11 +27,11 @@
@noescape _ body: () -> Result
) -> Result {
let oldMethod = class_getClassMethod(
- NSLocale.self, Selector("currentLocale"))
+ NSLocale.self, #selector(NSLocale.currentLocale))
precondition(oldMethod != nil, "could not find +[NSLocale currentLocale]")
let newMethod = class_getClassMethod(
- NSLocale.self, Selector("_swiftUnittest_currentLocale"))
+ NSLocale.self, #selector(NSLocale._swiftUnittest_currentLocale))
precondition(newMethod != nil, "could not find +[NSLocale _swiftUnittest_currentLocale]")
precondition(_temporaryNSLocaleCurrentLocale == nil,
diff --git a/test/ClangModules/objc_ir.swift b/test/ClangModules/objc_ir.swift
index 72c7ca7..d96ca24 100644
--- a/test/ClangModules/objc_ir.swift
+++ b/test/ClangModules/objc_ir.swift
@@ -11,9 +11,9 @@
import TestProtocols
import ObjCIRExtras
-// CHECK: @"\01L_selector_data(method:withFloat:)" = internal constant [18 x i8] c"method:withFloat:\00"
-// CHECK: @"\01L_selector_data(method:withDouble:)" = internal constant [19 x i8] c"method:withDouble:\00"
-// CHECK: @"\01L_selector_data(method:separateExtMethod:)" = internal constant [26 x i8] c"method:separateExtMethod:\00", section "__TEXT,__objc_methname,cstring_literals"
+// CHECK: @"\01L_selector_data(method:withFloat:)" = private global [18 x i8] c"method:withFloat:\00"
+// CHECK: @"\01L_selector_data(method:withDouble:)" = private global [19 x i8] c"method:withDouble:\00"
+// CHECK: @"\01L_selector_data(method:separateExtMethod:)" = private global [26 x i8] c"method:separateExtMethod:\00", section "__TEXT,__objc_methname,cstring_literals"
// Instance method invocation
// CHECK: define hidden void @_TF7objc_ir15instanceMethodsFCSo1BT_([[B]]*
diff --git a/test/ClangModules/objc_parse.swift b/test/ClangModules/objc_parse.swift
index 43fb81a..b9003bc 100644
--- a/test/ClangModules/objc_parse.swift
+++ b/test/ClangModules/objc_parse.swift
@@ -37,8 +37,8 @@
b.setEnabled(true)
// SEL
- b.performSelector("isEqual:", withObject:b)
- if let result = b.performSelector("getAsProto", withObject:nil) {
+ b.performSelector(#selector(NSObject.isEqual(_:)), withObject:b)
+ if let result = b.performSelector(#selector(B.getAsProto), withObject:nil) {
_ = result.takeUnretainedValue()
}
@@ -208,7 +208,7 @@
}
func testId(x: AnyObject) {
- x.performSelector!("foo:", withObject: x)
+ x.performSelector!("foo:", withObject: x) // expected-warning{{no method declared with Objective-C selector 'foo:'}}
x.performAdd(1, withValue: 2, withValue: 3, withValue2: 4)
x.performAdd!(1, withValue: 2, withValue: 3, withValue2: 4)
@@ -377,10 +377,10 @@
func testPropertyAndMethodCollision(obj: PropertyAndMethodCollision,
rev: PropertyAndMethodReverseCollision) {
obj.object = nil
- obj.object(obj, doSomething:"action")
+ obj.object(obj, doSomething:Selector("action"))
rev.object = nil
- rev.object(rev, doSomething:"action")
+ rev.object(rev, doSomething:Selector("action"))
var value: AnyObject = obj.protoProp()
value = obj.protoPropRO()
diff --git a/test/IDE/complete_pound_selector.swift b/test/IDE/complete_pound_selector.swift
new file mode 100644
index 0000000..74fe893
--- /dev/null
+++ b/test/IDE/complete_pound_selector.swift
@@ -0,0 +1,10 @@
+// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=AFTER_POUND | FileCheck %s
+
+// REQUIRES: objc_interop
+
+{
+ if ##^AFTER_POUND^#
+}
+
+// CHECK: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *)
+// CHECK: Keyword/ExprSpecific: selector({#@objc method#}); name=selector(@objc method)
diff --git a/test/IRGen/objc.swift b/test/IRGen/objc.swift
index c278ce2..b89ea97 100644
--- a/test/IRGen/objc.swift
+++ b/test/IRGen/objc.swift
@@ -18,8 +18,8 @@
// CHECK: [[RECT:%VSC4Rect]] = type
// CHECK: [[FLOAT:%Sf]] = type
-// CHECK: @"\01L_selector_data(bar)" = internal constant [4 x i8] c"bar\00", section "__TEXT,__objc_methname,cstring_literals", align 1
-// CHECK: @"\01L_selector(bar)" = internal global i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01L_selector_data(bar)", i64 0, i64 0), section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", align 8
+// CHECK: @"\01L_selector_data(bar)" = private global [4 x i8] c"bar\00", section "__TEXT,__objc_methname,cstring_literals", align 1
+// CHECK: @"\01L_selector(bar)" = private externally_initialized global i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01L_selector_data(bar)", i64 0, i64 0), section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", align 8
// CHECK: @_TMnVSC4Rect = linkonce_odr hidden constant
// CHECK: @_TMVSC4Rect = linkonce_odr hidden global
diff --git a/test/IRGen/objc_selector.sil b/test/IRGen/objc_selector.sil
new file mode 100644
index 0000000..9dfb851
--- /dev/null
+++ b/test/IRGen/objc_selector.sil
@@ -0,0 +1,19 @@
+// RUN: %target-swift-frontend -emit-ir %s | FileCheck %s
+
+// REQUIRES: objc_interop
+
+sil_stage canonical
+
+import Builtin
+
+// CHECK: @"\01L_selector_data(help:me:)" = private global [9 x i8] c"help:me:\00", section "__TEXT,__objc_methname,cstring_literals"
+// CHECK: @"\01L_selector(help:me:)" = private externally_initialized global i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"\01L_selector_data(help:me:)", {{i(32|64)}} 0, {{i(32|64)}} 0), section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"
+
+// CHECK-LABEL: define i8* @objc_selector_literal() #0 {
+sil @objc_selector_literal : $@convention(thin) () -> Builtin.RawPointer {
+bb0:
+ // CHECK: [[SEL:%[0-9]+]] = load i8*, i8** @"\01L_selector(help:me:)"
+ %0 = string_literal objc_selector "help:me:"
+ // CHECK-NEXT: ret i8* [[SEL]]
+ return %0 : $Builtin.RawPointer
+}
diff --git a/test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift b/test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift
index 79db467..11a4afd 100644
--- a/test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift
+++ b/test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift
@@ -221,6 +221,10 @@
public var _code: Int { return code }
}
+extension NSArray {
+ @objc(methodIntroducedInOverlay) public func introducedInOverlay() { }
+}
+
@_silgen_name("swift_convertNSErrorToErrorType")
func _convertNSErrorToErrorType(string: NSError?) -> ErrorType
diff --git a/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift b/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
index bfb5f66..8ff5b9e 100644
--- a/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
+++ b/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
@@ -46,6 +46,10 @@
public struct Selector : StringLiteralConvertible {
private var ptr : COpaquePointer
+ public init(_ value: String) {
+ self.init(stringLiteral: value)
+ }
+
public init(unicodeScalarLiteral value: String) {
self.init(stringLiteral: value)
}
diff --git a/test/Interpreter/SDK/objc_extensions.swift b/test/Interpreter/SDK/objc_extensions.swift
index 19e9bdd..3fd32ac 100644
--- a/test/Interpreter/SDK/objc_extensions.swift
+++ b/test/Interpreter/SDK/objc_extensions.swift
@@ -40,6 +40,10 @@
// CHECK: true
print(o.respondsToSelector("setBlackHoleWithHawkingRadiation:"))
+// Test #selector for referring to methods.
+// CHECK: true
+print(o.respondsToSelector(#selector(NSObject.frob)))
+
// CHECK: I've been frobbed!
o.frob()
// CHECK: true
diff --git a/test/SILGen/objc_selector.swift b/test/SILGen/objc_selector.swift
new file mode 100644
index 0000000..7ffa4e9
--- /dev/null
+++ b/test/SILGen/objc_selector.swift
@@ -0,0 +1,19 @@
+// RUN: %target-swift-frontend -emit-sil -sdk %S/Inputs -I %S/Inputs -enable-source-import %s | FileCheck %s
+
+// REQUIRES: objc_interop
+
+import ObjectiveC
+import Foundation
+
+class Foo {
+ @objc(methodForInt:) func method(a: Int32) { }
+}
+
+// CHECK-LABEL: sil hidden @_TF13objc_selector14createSelector
+func createSelector(foo: Foo) -> Selector {
+ // CHECK: [[LITERAL:%[0-9]+]] = string_literal objc_selector "methodForInt:"
+ // CHECK-NEXT: [[PTR:%[0-9]+]] = struct $COpaquePointer ([[LITERAL]] : $Builtin.RawPointer)
+ // CHECK-NEXT: [[SEL:%[0-9]+]] = struct $Selector (%3 : $COpaquePointer)
+ // CHECK-: return [[SEL]] : $Selector
+ return #selector(foo.method)
+}
diff --git a/test/SILOptimizer/devirt_speculative.sil b/test/SILOptimizer/devirt_speculative.sil
new file mode 100644
index 0000000..99fc383
--- /dev/null
+++ b/test/SILOptimizer/devirt_speculative.sil
@@ -0,0 +1,106 @@
+// RUN: %target-sil-opt -enable-sil-verify-all %s -specdevirt | FileCheck %s
+
+sil_stage canonical
+
+@objc class MyNSObject {}
+private class Base : MyNSObject {
+ override init()
+ @inline(never) func foo()
+}
+
+private class Sub : Base {
+ override init()
+ @inline(never) override func foo()
+}
+
+sil private [noinline] @_TBaseFooFun : $@convention(method) (@guaranteed Base) -> () {
+bb0(%0 : $Base):
+ %1 = tuple()
+ return %1 : $()
+}
+
+sil private [noinline] @_TSubFooFun : $@convention(method) (@guaranteed Sub) -> () {
+bb0(%0 : $Sub):
+ %1 = tuple()
+ return %1 : $()
+}
+
+sil_vtable Base {
+ #Base.foo!1: _TBaseFooFun
+}
+
+sil_vtable Sub {
+ #Base.foo!1: _TSubFooFun
+}
+
+sil @test_objc_ancestry : $@convention(thin) (@guaranteed Base) -> () {
+bb0(%0: $Base):
+ %1 = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> () , $@convention(method) (@guaranteed Base) -> ()
+ %2 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> ()
+ %3 = tuple()
+ return %3 : $()
+}
+
+// Make sure we leave the generic method call because an objc derived class can be extended at runtime.
+
+// CHECK-LABEL: sil @test_objc_ancestry
+// CHECK: bb0
+// CHECK: [[METH:%.*]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> () , $@convention(method) (@guaranteed Base) -> ()
+// CHECK: checked_cast_br [exact] %0 : $Base to $Base, bb{{.*}}, bb[[CHECK2:[0-9]+]]
+// CHECK: bb[[CHECK2]]{{.*}}:
+// CHECK: checked_cast_br [exact] %0 : $Base to $Sub, bb{{.*}}, bb[[GENCALL:[0-9]+]]
+// CHECK: bb[[GENCALL]]{{.*}}:
+// CHECK: apply [[METH]]
+
+struct MyValue {}
+
+private class Generic<T> : MyNSObject {
+ override init()
+}
+
+private class Base2 : Generic<MyValue> {
+ override init()
+ @inline(never) func foo()
+}
+
+private class Sub2 : Base2 {
+ override init()
+ @inline(never) override func foo()
+}
+
+sil private [noinline] @_TBase2FooFun : $@convention(method) (@guaranteed Base2) -> () {
+bb0(%0 : $Base2):
+ %1 = tuple()
+ return %1 : $()
+}
+
+sil private [noinline] @_TSub2FooFun : $@convention(method) (@guaranteed Sub2) -> () {
+bb0(%0 : $Sub2):
+ %1 = tuple()
+ return %1 : $()
+}
+
+sil_vtable Base2 {
+ #Base2.foo!1: _TBase2FooFun
+}
+
+sil_vtable Sub2 {
+ #Base2.foo!1: _TSub2FooFun
+}
+
+sil @test_objc_ancestry2 : $@convention(thin) (@guaranteed Base2) -> () {
+bb0(%0: $Base2):
+ %1 = class_method %0 : $Base2, #Base2.foo!1 : (Base2) -> () -> () , $@convention(method) (@guaranteed Base2) -> ()
+ %2 = apply %1(%0) : $@convention(method) (@guaranteed Base2) -> ()
+ %3 = tuple()
+ return %3 : $()
+}
+
+// CHECK-LABEL: sil @test_objc_ancestry2
+// CHECK: bb0
+// CHECK: [[METH:%.*]] = class_method %0 : $Base2, #Base2.foo!1 : (Base2) -> () -> () , $@convention(method) (@guaranteed Base2) -> ()
+// CHECK: checked_cast_br [exact] %0 : $Base2 to $Base2, bb{{.*}}, bb[[CHECK2:[0-9]+]]
+// CHECK: bb[[CHECK2]]{{.*}}:
+// CHECK: checked_cast_br [exact] %0 : $Base2 to $Sub2, bb{{.*}}, bb[[GENCALL:[0-9]+]]
+// CHECK: bb[[GENCALL]]{{.*}}:
+// CHECK: apply [[METH]]
diff --git a/test/SourceKit/CodeComplete/complete_filter.swift b/test/SourceKit/CodeComplete/complete_filter.swift
index 9856628..7684e34 100644
--- a/test/SourceKit/CodeComplete/complete_filter.swift
+++ b/test/SourceKit/CodeComplete/complete_filter.swift
@@ -106,3 +106,20 @@
// GROUP-NEXT: ]
// GROUP-LABEL: Results for filterText: overloadp [
// GROUP-NEXT: ]
+
+struct UnnamedArgs {
+ func dontMatchAgainst(unnamed: Int, arguments: Int, _ unnamed2:Int) {}
+ func test() {
+ self.#^UNNAMED_ARGS_0,dont,arguments,unnamed^#
+ }
+}
+
+// RUN: %complete-test -tok=UNNAMED_ARGS_0 %s | FileCheck %s -check-prefix=UNNAMED_ARGS_0
+// UNNAMED_ARGS_0: Results for filterText: dont [
+// UNNAMED_ARGS_0-NEXT: dontMatchAgainst(unnamed: Int, arguments: Int, unnamed2: Int)
+// UNNAMED_ARGS_0-NEXT: ]
+// UNNAMED_ARGS_0-NEXT: Results for filterText: arguments [
+// UNNAMED_ARGS_0-NEXT: dontMatchAgainst(unnamed: Int, arguments: Int, unnamed2: Int)
+// UNNAMED_ARGS_0-NEXT: ]
+// UNNAMED_ARGS_0-NEXT: Results for filterText: unnamed [
+// UNNAMED_ARGS_0-NEXT: ]
diff --git a/test/SourceKit/CodeComplete/complete_member.swift.response b/test/SourceKit/CodeComplete/complete_member.swift.response
index bc604ec..e508eb0 100644
--- a/test/SourceKit/CodeComplete/complete_member.swift.response
+++ b/test/SourceKit/CodeComplete/complete_member.swift.response
@@ -13,7 +13,7 @@
},
{
key.kind: source.lang.swift.decl.function.method.instance,
- key.name: "fooInstanceFunc1(a:)",
+ key.name: "fooInstanceFunc1(:)",
key.sourcetext: "fooInstanceFunc1(<#T##a: Int##Int#>)",
key.description: "fooInstanceFunc1(a: Int)",
key.typename: "Double",
diff --git a/test/SourceKit/CodeComplete/complete_moduleimportdepth.swift b/test/SourceKit/CodeComplete/complete_moduleimportdepth.swift
index 521fdb3..c6d30a1 100644
--- a/test/SourceKit/CodeComplete/complete_moduleimportdepth.swift
+++ b/test/SourceKit/CodeComplete/complete_moduleimportdepth.swift
@@ -9,7 +9,7 @@
// RUN: FileCheck %s < %t
// Swift == 1
-// CHECK-LABEL: key.name: "abs(x:)",
+// CHECK-LABEL: key.name: "abs(:)",
// CHECK-NEXT: key.sourcetext: "abs(<#T##x: T##T#>)",
// CHECK-NEXT: key.description: "abs(x: T)",
// CHECK-NEXT: key.typename: "T",
@@ -22,7 +22,7 @@
// CHECK-NEXT: },
// FooHelper.FooHelperExplicit == 1
-// CHECK-LABEL: key.name: "fooHelperExplicitFrameworkFunc1(a:)",
+// CHECK-LABEL: key.name: "fooHelperExplicitFrameworkFunc1(:)",
// CHECK-NEXT: key.sourcetext: "fooHelperExplicitFrameworkFunc1(<#T##a: Int32##Int32#>)",
// CHECK-NEXT: key.description: "fooHelperExplicitFrameworkFunc1(a: Int32)",
// CHECK-NEXT: key.typename: "Int32",
diff --git a/test/SourceKit/CodeComplete/complete_name.swift b/test/SourceKit/CodeComplete/complete_name.swift
index 9dfced4..fb606da 100644
--- a/test/SourceKit/CodeComplete/complete_name.swift
+++ b/test/SourceKit/CodeComplete/complete_name.swift
@@ -2,16 +2,19 @@
// RUN: %complete-test -raw -tok=METHOD_NAME %s | FileCheck %s -check-prefix=METHOD_NAME
struct S {
- init(a: Int, b: Int) {}
- func foo(a: Int, b: Int) {}
+ init(a: Int, b: Int, _ c: Int) {}
+ init(_ a: Int, _ b: Int) {}
+ func foo1(a: Int, _ b: Int, _ c: Int) {}
+ func foo2(a a: Int, b: Int, c: Int) {}
}
func test01() {
S(#^INIT_NAME^#)
}
-// INIT_NAME: key.name: "a:b:)"
+// INIT_NAME: key.name: "a:b::)"
func test02(x: S) {
x.#^METHOD_NAME^#
}
-// METHOD_NAME: key.name: "foo(a:b:)"
+// METHOD_NAME: key.name: "foo1(:::)"
+// METHOD_NAME: key.name: "foo2(a:b:c:)"
diff --git a/test/SourceKit/CodeComplete/complete_with_closure_param.swift b/test/SourceKit/CodeComplete/complete_with_closure_param.swift
index 1886871..d8ad143 100644
--- a/test/SourceKit/CodeComplete/complete_with_closure_param.swift
+++ b/test/SourceKit/CodeComplete/complete_with_closure_param.swift
@@ -9,12 +9,12 @@
// RUN: %sourcekitd-test -req=complete -pos=7:5 %s -- %s | FileCheck %s
// CHECK: key.kind: source.lang.swift.decl.function.method.instance,
-// CHECK-NEXT: key.name: "foo(x:)",
+// CHECK-NEXT: key.name: "foo(:)",
// CHECK-NEXT: key.sourcetext: "foo(<#T##x: Int -> Int##Int -> Int#>)",
// CHECK-NEXT: key.description: "foo(x: Int -> Int)",
// CHECK-NEXT: key.typename: "Void",
// CHECK: key.kind: source.lang.swift.decl.function.method.instance,
-// CHECK-NEXT: key.name: "foo2(x:)",
+// CHECK-NEXT: key.name: "foo2(:)",
// CHECK-NEXT: key.sourcetext: "foo2(<#T##x: MyFnTy##MyFnTy##Int -> Int#>)",
// CHECK-NEXT: key.description: "foo2(x: MyFnTy)",
diff --git a/test/SourceKit/CodeFormat/indent-computed-property.swift b/test/SourceKit/CodeFormat/indent-computed-property.swift
index 7ed582a..4614d6a 100644
--- a/test/SourceKit/CodeFormat/indent-computed-property.swift
+++ b/test/SourceKit/CodeFormat/indent-computed-property.swift
@@ -16,6 +16,22 @@
}
}
+class C1 {
+ var total: Int = 0 {
+didSet {
+print()
+ }
+ }
+}
+
+class C2 {
+ var total: Int = 0 {
+ didSet {
+print()
+ }
+ }
+}
+
// RUN: %sourcekitd-test -req=format -line=1 -length=1 %s >%t.response
// RUN: %sourcekitd-test -req=format -line=2 -length=1 %s >>%t.response
// RUN: %sourcekitd-test -req=format -line=3 -length=1 %s >>%t.response
@@ -29,6 +45,8 @@
// RUN: %sourcekitd-test -req=format -line=14 -length=1 %s >>%t.response
// RUN: %sourcekitd-test -req=format -line=15 -length=1 %s >>%t.response
// RUN: %sourcekitd-test -req=format -line=16 -length=1 %s >>%t.response
+// RUN: %sourcekitd-test -req=format -line=21 -length=1 %s >>%t.response
+// RUN: %sourcekitd-test -req=format -line=30 -length=1 %s >>%t.response
// RUN: FileCheck --strict-whitespace %s <%t.response
// CHECK: key.sourcetext: "class Foo {"
@@ -45,3 +63,6 @@
// CHECK: key.sourcetext: " {"
// CHECK: key.sourcetext: " return 0"
// CHECK: key.sourcetext: " }"
+// CHECK: key.sourcetext: " didSet {"
+ " didSet {"
+// CHECK: key.sourcetext: " print()"
diff --git a/test/SourceKit/CodeFormat/indent-sibling.swift b/test/SourceKit/CodeFormat/indent-sibling.swift
index 4407b9f..ecd2d8a 100644
--- a/test/SourceKit/CodeFormat/indent-sibling.swift
+++ b/test/SourceKit/CodeFormat/indent-sibling.swift
@@ -1,41 +1,39 @@
-// XFAIL: *
-
class Foo {
func foo(Value1 : Int,
Value2 : Int) {
- }
+ }
func bar(value1 : Int,
value2 : Int,
- value3 : Int) {
- }
- func foo1(Value1 : Int,
- Value2 : Int) {}
- func foo3() {
- foo(1,
- 2)
- foo(2,
+ value3 : Int) {
+ }
+ func foo1(Value1 : Int,
+ Value2 : Int) {}
+ func foo3() {
+ foo(1,
+ 2)
+ foo(2,
3)
- foo(3,
+ foo(3,
4)
- }
- func intGen() -> Int { return 0 }
- func foo4() {
- var a = [1,
- 2,
- 3,
- 1 + 2,
- intGen()]
- }
- func foo5() {
- var a = [1 : 1,
- 2 : 2,
- 3 + 2 : 3,
- intGen() : intGen()]
+ }
+ func intGen() -> Int { return 0 }
+ func foo4() {
+ var a = [1,
+ 2,
+ 3,
+ 1 + 2,
+ intGen()]
+ }
+ func foo5() {
+ var a = [1 : 1,
+ 2 : 2,
+ 3 + 2 : 3,
+ intGen() : intGen()]
var b = (2,
3,
4,
5)
- }
+ }
func foo6<T1: Testable,
T2: Testable,
T3: Testable,
@@ -112,38 +110,38 @@
// "value2 : Int,"
// CHECK: key.sourcetext: "value3 : Int) {"
-// " func foo1(Value1 : Int,"
-// CHECK: key.sourcetext: " Value2 : Int) {}"
+// " func foo1(Value1 : Int,"
+// CHECK: key.sourcetext: " Value2 : Int) {}"
-// " foo(1,"
-// CHECK: key.sourcetext: " 2)"
+// " foo(1,"
+// CHECK: key.sourcetext: " 2)"
-// " foo(2,"
-// CHECK: key.sourcetext: " 3)"
+// " foo(2,"
+// CHECK: key.sourcetext: " 3)"
-// " foo(3,"
-// CHECK: key.sourcetext: " 4)"
+// " foo(3,"
+// CHECK: key.sourcetext: " 4)"
-// " var a = [1,"
-// CHECK: key.sourcetext: " 2,"
+// " var a = [1,"
+// CHECK: key.sourcetext: " 2,"
-// " 2,"
-// CHECK: key.sourcetext: " 3,"
+// " 2,"
+// CHECK: key.sourcetext: " 3,"
-// " 3,"
-// CHECK: key.sourcetext: " 1 + 2,"
+// " 3,"
+// CHECK: key.sourcetext: " 1 + 2,"
-// " 1 + 2,"
-// CHECK: key.sourcetext: " intGen()]"
+// " 1 + 2,"
+// CHECK: key.sourcetext: " intGen()]"
-// " var a = [1 : 1,"
-// CHECK: key.sourcetext: " 2 : 2,"
+// " var a = [1 : 1,"
+// CHECK: key.sourcetext: " 2 : 2,"
-// " 2 : 2,"
-// CHECK: key.sourcetext: " 3 + 2 : 3,"
+// " 2 : 2,"
+// CHECK: key.sourcetext: " 3 + 2 : 3,"
-// " 3 + 2 : 3,"
-// CHECK: key.sourcetext: " intGen() : intGen()]"
+// " 3 + 2 : 3,"
+// CHECK: key.sourcetext: " intGen() : intGen()]"
// " var b = (2,"
// CHECK: key.sourcetext: " 3,"
diff --git a/test/expr/unary/selector/fixits.swift b/test/expr/unary/selector/fixits.swift
new file mode 100644
index 0000000..f80174a
--- /dev/null
+++ b/test/expr/unary/selector/fixits.swift
@@ -0,0 +1,107 @@
+// REQUIRES: objc_interop
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: rm -rf %t.overlays
+// RUN: mkdir -p %t.overlays
+
+// FIXME: BEGIN -enable-source-import hackaround
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t.overlays %clang-importer-sdk-path/swift-modules/ObjectiveC.swift
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t.overlays %clang-importer-sdk-path/swift-modules/CoreGraphics.swift
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t.overlays %clang-importer-sdk-path/swift-modules/Foundation.swift
+// FIXME: END -enable-source-import hackaround
+
+// Make sure we get the right diagnostics.
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t.overlays) -parse %s -verify
+
+// Copy the source, apply the Fix-Its, and compile it again, making
+// sure that we've cleaned up all of the deprecation warnings.
+// RUN: mkdir -p %t.sources
+// RUN: mkdir -p %t.remapping
+// RUN: cp %s %t.sources/fixits.swift
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t.overlays) -parse %t.sources/fixits.swift -fixit-all -emit-fixits-path %t.remapping/fixits.remap
+// RUN: %S/../../../../utils/apply-fixit-edits.py %t.remapping
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t.overlays) -parse %t.sources/fixits.swift 2> %t.result
+
+// RUN: FileCheck %s < %t.result
+// RUN: grep -c "warning:" %t.result | grep 4
+
+// CHECK: warning: no method declared with Objective-C selector 'unknownMethodWithValue:label:'
+// CHECK: warning: string literal is not a valid Objective-C selector
+// CHECK: warning: no method declared with Objective-C selector 'unknownMethodWithValue:label:'
+// CHECK: warning: string literal is not a valid Objective-C selector
+
+import Foundation
+
+class Bar : Foo {
+ @objc(method2WithValue:) override func method2(value: Int) { }
+
+ @objc(overloadedWithInt:) func overloaded(x: Int) { }
+ @objc(overloadedWithString:) func overloaded(x: String) { }
+
+ @objc(staticOverloadedWithInt:) static func staticOverloaded(x: Int) { }
+ @objc(staticOverloadedWithString:) static func staticOverloaded(x: String) { }
+
+ @objc(staticOrNonStatic:) func staticOrNonStatic(x: Int) { }
+ @objc(staticOrNonStatic:) static func staticOrNonStatic(x: Int) { }
+
+ @objc(theInstanceOne:) func staticOrNonStatic2(x: Int) { }
+ @objc(theStaticOne:) static func staticOrNonStatic2(x: Int) { }
+}
+
+class Foo {
+ @objc(methodWithValue:label:) func method(value: Int, label: String) { }
+
+ @objc(method2WithValue:) func method2(value: Int) { }
+
+ @objc func method3() { }
+
+ @objc var property: String = ""
+}
+
+
+func testDeprecatedStringLiteralSelector() {
+ let sel1: Selector = "methodWithValue:label:" // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{24-48=#selector(Foo.method(_:label:))}}
+ _ = sel1
+
+ _ = "methodWithValue:label:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-43=#selector(Foo.method(_:label:))}}
+ _ = "property" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' or explicitly construct a 'Selector'}}{{7-7=Selector(}}{{17-29=)}}
+ _ = "setProperty:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' or explicitly construct a 'Selector'}}{{7-7=Selector(}}{{21-33=)}}
+ _ = "unknownMethodWithValue:label:" as Selector // expected-warning{{no method declared with Objective-C selector 'unknownMethodWithValue:label:'}}{{7-7=Selector(}}{{38-50=)}}
+ _ = "badSelector:label" as Selector // expected-warning{{string literal is not a valid Objective-C selector}}
+ _ = "method2WithValue:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-38=#selector(Foo.method2(_:))}}
+ _ = "method3" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-28=#selector(Foo.method3)}}
+
+ // Overloaded cases
+ _ = "overloadedWithInt:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-39=#selector(Bar.overloaded(_:) as (Bar) -> (Int) -> ())}}
+ _ = "overloadedWithString:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-42=#selector(Bar.overloaded(_:) as (Bar) -> (String) -> ())}}
+
+ _ = "staticOverloadedWithInt:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-45=#selector(Bar.staticOverloaded(_:) as (Int) -> ())}}
+ _ = "staticOverloadedWithString:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-48=#selector(Bar.staticOverloaded(_:) as (String) -> ())}}
+
+ // We don't need coercion here because we get the right selector
+ // from the static method.
+ _ = "staticOrNonStatic:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-39=#selector(Bar.staticOrNonStatic(_:))}}
+
+ // We need coercion here because we asked for a selector from an
+ // instance method with the same name as (but a different selector
+ // from) a static method.
+ _ = "theInstanceOne:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-36=#selector(Bar.staticOrNonStatic2(_:) as (Bar) -> (Int) -> ())}}
+
+ // Note: from Foundation
+ _ = "initWithArray:" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-35=#selector(NSSet.init(array:))}}
+
+ // Note: from Foundation overlay
+ _ = "methodIntroducedInOverlay" as Selector // expected-warning{{use of string literal for Objective-C selectors is deprecated; use '#selector' instead}}{{7-46=#selector(NSArray.introducedInOverlay)}}
+}
+
+func testSelectorConstruction() {
+ _ = Selector("methodWithValue:label:") // expected-warning{{use '#selector' instead of explicitly constructing a 'Selector'}}{{7-41=#selector(Foo.method(_:label:))}}
+ _ = Selector("unknownMethodWithValue:label:") // expected-warning{{no method declared with Objective-C selector 'unknownMethodWithValue:label:'}}
+ _ = Selector("badSelector:label") // expected-warning{{string literal is not a valid Objective-C selector}}
+ _ = Selector("method2WithValue:") // expected-warning{{use '#selector' instead of explicitly constructing a 'Selector'}}{{7-36=#selector(Foo.method2(_:))}}
+ _ = Selector("method3") // expected-warning{{use '#selector' instead of explicitly constructing a 'Selector'}}{{7-26=#selector(Foo.method3)}}
+
+ // Note: from Foundation
+ _ = Selector("initWithArray:") // expected-warning{{use '#selector' instead of explicitly constructing a 'Selector'}}{{7-33=#selector(NSSet.init(array:))}}
+}
diff --git a/test/expr/unary/selector/selector.swift b/test/expr/unary/selector/selector.swift
new file mode 100644
index 0000000..bf3c55d
--- /dev/null
+++ b/test/expr/unary/selector/selector.swift
@@ -0,0 +1,102 @@
+// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-objc-attr-requires-foundation-module -parse %s -verify
+import ObjectiveC
+
+// REQUIRES: objc_interop
+
+@objc class A { }
+@objc class B { }
+
+class C1 {
+ @objc init(a: A, b: B) { }
+
+ @objc func method1(a: A, b: B) { }
+ @objc(someMethodWithA:B:) func method2(a: A, b: B) { }
+
+ @objc class func method3(a: A, b: B) { } // expected-note{{found this candidate}}
+ @objc class func method3(a a: A, b: B) { } // expected-note{{found this candidate}}
+
+ @objc var a: A = A() // expected-note{{'a' declared here}}
+
+ @objc func getC1() -> AnyObject { return self }
+}
+
+@objc protocol P1 {
+ func method4(a: A, b: B)
+ static func method5(a: B, b: B)
+}
+
+extension C1 {
+ final func method6() { } // expected-note{{add '@objc' to expose this method to Objective-C}}{{3-3=@objc }}
+}
+
+func testSelector(c1: C1, p1: P1, obj: AnyObject) {
+ // Instance methods on an instance
+ let sel1 = #selector(c1.method1)
+ _ = #selector(c1.method1(_:b:))
+ _ = #selector(c1.method2)
+
+ // Instance methods on a class.
+ _ = #selector(C1.method1)
+ _ = #selector(C1.method1(_:b:))
+ _ = #selector(C1.method2)
+
+ // Class methods on a class.
+ _ = #selector(C1.method3(_:b:))
+ _ = #selector(C1.method3(a:b:))
+
+ // Methods on a protocol.
+ _ = #selector(P1.method4)
+ _ = #selector(P1.method4(_:b:))
+ _ = #selector(P1.method5) // FIXME: expected-error{{static member 'method5' cannot be used on instance of type 'P1.Protocol'}}
+ _ = #selector(P1.method5(_:b:)) // FIXME: expected-error{{static member 'method5(_:b:)' cannot be used on instance of type 'P1.Protocol'}}
+ _ = #selector(p1.method4)
+ _ = #selector(p1.method4(_:b:))
+ _ = #selector(p1.dynamicType.method5)
+ _ = #selector(p1.dynamicType.method5(_:b:))
+
+ // Interesting expressions that refer to methods.
+ _ = #selector(Swift.AnyObject.method1)
+ _ = #selector(AnyObject.method1!)
+ _ = #selector(obj.getC1?().method1)
+
+ // Initializers
+ _ = #selector(C1.init(a:b:))
+
+ // Make sure the result has type "ObjectiveC.Selector"
+ let sel2: Selector
+ sel2 = sel1
+ _ = sel2
+}
+
+func testAmbiguity() {
+ _ = #selector(C1.method3) // expected-error{{ambiguous use of 'method3(_:b:)'}}
+}
+
+func testProperties(c1: C1) {
+ _ = #selector(c1.a) // expected-error{{argument of '#selector' cannot refer to a property}}
+ _ = #selector(C1.a) // FIXME poor diagnostic: expected-error{{instance member 'a' cannot be used on type 'C1'}}
+}
+
+func testNonObjC(c1: C1) {
+ _ = #selector(c1.method6) // expected-error{{argument of '#selector' refers to a method that is not exposed to Objective-C}}
+}
+
+func testParseErrors1() {
+ #selector foo // expected-error{{expected '(' following '#selector'}}
+}
+
+func testParseErrors2() {
+ #selector( // expected-error{{expected expression naming a method within '#selector(...)'}}
+}
+
+func testParseErrors3(c1: C1) {
+ #selector( // expected-note{{to match this opening '('}}
+ c1.method1(_:b:) // expected-error{{expected ')' to complete '#selector' expression}}
+}
+
+func testParseErrors4() {
+ // Subscripts
+ _ = #selector(C1.subscript) // expected-error{{expected member name following '.'}}
+ // expected-error@-1{{consecutive statements on a line must be separated by ';'}}
+ // expected-error@-2{{expected '(' for subscript parameters}}
+}
diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp
index 34a4663..b459247 100644
--- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp
+++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp
@@ -873,6 +873,7 @@
bool shouldPrint = !C.isAnnotation();
switch (C.getKind()) {
case ChunkKind::TypeAnnotation:
+ case ChunkKind::CallParameterInternalName:
case ChunkKind::CallParameterClosureType:
case ChunkKind::CallParameterType:
case ChunkKind::DeclAttrParamEqual:
diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp
index 057528c..0edbf10 100644
--- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp
+++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp
@@ -1642,7 +1642,8 @@
// }
if (auto VD = dyn_cast_or_null<VarDecl>(Cursor->getAsDecl())) {
if (auto Getter = VD->getGetter()) {
- if (Getter->getAccessorKeywordLoc().isInvalid()) {
+ if (!Getter->isImplicit() &&
+ Getter->getAccessorKeywordLoc().isInvalid()) {
LineAndColumn = ParentLineAndColumn;
continue;
}
@@ -1898,7 +1899,7 @@
if (EleStart.isInvalid()) {
EleStart = TE->getElement(I)->getStartLoc();
}
- addPair(TE->getElement(I)->getEndLoc(), EleStart, tok::comma);
+ addPair(TE->getElement(I)->getEndLoc(), FindAlignLoc(EleStart), tok::comma);
}
}
@@ -1916,8 +1917,9 @@
// Function parameters are siblings.
for (auto P : AFD->getParameterLists()) {
for (auto param : *P) {
- addPair(param->getEndLoc(),
- FindAlignLoc(param->getStartLoc()), tok::comma);
+ if (!param->isSelfParameter())
+ addPair(param->getEndLoc(), FindAlignLoc(param->getStartLoc()),
+ tok::comma);
}
}
}
diff --git a/utils/build-presets.ini b/utils/build-presets.ini
index 916c3be..6911bcc 100644
--- a/utils/build-presets.ini
+++ b/utils/build-presets.ini
@@ -582,3 +582,16 @@
release-debuginfo
assertions
+
+#===------------------------------------------------------------------------===#
+# Test all platforms on OS X builder
+#===------------------------------------------------------------------------===#
+
+[preset: buildbot_all_platforms,tools=RA,stdlib=RA]
+mixin-preset=buildbot_incremental_base
+
+build-subdir=buildbot_incremental
+
+# Build Release without debug info, because it is faster to build.
+release
+assertions
diff --git a/utils/build-script-impl b/utils/build-script-impl
index 07d6b53..52a2edd 100755
--- a/utils/build-script-impl
+++ b/utils/build-script-impl
@@ -378,6 +378,7 @@
-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="${cmake_osx_deployment_target}"
-DCMAKE_OSX_SYSROOT:PATH="$(xcrun --sdk ${xcrun_sdk_name} --show-sdk-path)"
-DLLVM_HOST_TRIPLE:STRING="${llvm_host_triple}"
+ -DLLVM_ENABLE_LIBCXX:BOOL=TRUE
)
swift_cmake_options=(
"${swift_cmake_options[@]}"
@@ -385,6 +386,7 @@
-DSWIFT_DARWIN_DEPLOYMENT_VERSION_IOS="${DARWIN_DEPLOYMENT_VERSION_IOS}"
-DSWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS="${DARWIN_DEPLOYMENT_VERSION_TVOS}"
-DSWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS="${DARWIN_DEPLOYMENT_VERSION_WATCHOS}"
+ -DLLVM_ENABLE_LIBCXX:BOOL=TRUE
)
if [[ "${llvm_target_arch}" ]] ; then
diff --git a/utils/update-checkout b/utils/update-checkout
index a0ba5d8..a8a262b 100755
--- a/utils/update-checkout
+++ b/utils/update-checkout
@@ -21,41 +21,49 @@
SWIFT_SOURCE_ROOT,
WorkingDirectory,
check_call,
+ check_output,
)
-def update_working_copy(repo_path):
+def update_working_copy(repo_path, branch):
if not os.path.isdir(repo_path):
return
print("--- Updating '" + repo_path + "' ---")
with WorkingDirectory(repo_path):
+ if branch:
+ status = check_output(['git', 'status', '--porcelain'])
+ if status:
+ print("Please, commit your changes.")
+ print(status)
+ exit(1)
+ check_call(['git', 'checkout', branch])
+
# Prior to Git 2.6, this is the way to do a "git pull
# --rebase" that respects rebase.autostash. See
# http://stackoverflow.com/a/30209750/125349
- check_call([ "git", "fetch" ])
- check_call([ "git", "rebase", "FETCH_HEAD" ])
+ check_call(["git", "fetch"])
+ check_call(["git", "rebase", "FETCH_HEAD"])
-def obtain_additional_swift_sources(opts = {'with_ssh': False}):
+def obtain_additional_swift_sources(with_ssh, branch):
additional_repos = {
'llvm': 'apple/swift-llvm',
'clang': 'apple/swift-clang',
'lldb': 'apple/swift-lldb',
'cmark': 'apple/swift-cmark',
- 'llbuild': 'apple/swift-llbuild',
- 'swiftpm': 'apple/swift-package-manager',
- 'swift-corelibs-xctest': 'apple/swift-corelibs-xctest',
- 'swift-corelibs-foundation': 'apple/swift-corelibs-foundation',
'swift-integration-tests': 'apple/swift-integration-tests',
}
for dir_name, repo in additional_repos.items():
with WorkingDirectory(SWIFT_SOURCE_ROOT):
if not os.path.isdir(os.path.join(dir_name, ".git")):
print("--- Cloning '" + dir_name + "' ---")
- if opts['with_ssh'] is True:
+ if with_ssh is True:
remote = "git@github.com:" + repo + '.git'
else:
remote = "https://github.com/" + repo + '.git'
check_call(['git', 'clone', remote, dir_name])
+ if branch:
+ src_path = SWIFT_SOURCE_ROOT + "/" + dir_name + "/" + ".git"
+ check_call(['git', '--git-dir', src_path, '--work-tree', os.path.join(SWIFT_SOURCE_ROOT, dir_name), 'checkout', branch])
def main():
parser = argparse.ArgumentParser(
@@ -63,9 +71,9 @@
description="""
repositories.
-By default, updates your checkouts of Swift, SourceKit, LLDB, and SwiftPM.""")
+By default, updates your checkouts of Swift, and LLDB.""")
parser.add_argument("-a", "--all",
- help="also update checkouts of llbuild, LLVM, and Clang",
+ help="also update checkouts of LLVM, and Clang",
action="store_true")
parser.add_argument("--clone",
help="Obtain Sources for Swift and Related Projects",
@@ -73,31 +81,28 @@
parser.add_argument("--clone-with-ssh",
help="Obtain Sources for Swift and Related Projects via SSH",
action="store_true")
+ parser.add_argument("--branch",
+ help="Obtain Sources for specific branch")
args = parser.parse_args()
- if args.clone:
- obtain_additional_swift_sources()
- return 0
+ clone = args.clone
+ clone_with_ssh = args.clone_with_ssh
+ branch = args.branch
- if args.clone_with_ssh:
- obtain_additional_swift_sources({'with_ssh': True})
+ if clone or clone_with_ssh:
+ obtain_additional_swift_sources(clone_with_ssh, branch)
return 0
if args.all:
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "llbuild"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "llvm"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "clang"))
+ update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "llvm"), branch)
+ update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "clang"), branch)
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "swift"))
+ update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "swift"), branch)
update_working_copy(
- os.path.join(SWIFT_SOURCE_ROOT, "swift", "benchmark", "PerfTestSuite"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "SourceKit"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "cmark"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "lldb"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "swiftpm"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "swift-corelibs-foundation"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "swift-corelibs-xctest"))
- update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "swift-integration-tests"))
+ os.path.join(SWIFT_SOURCE_ROOT, "swift", "benchmark", "PerfTestSuite"), branch)
+ update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "cmark"), branch)
+ update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "lldb"), branch)
+ update_working_copy(os.path.join(SWIFT_SOURCE_ROOT, "swift-integration-tests"), branch)
return 0