SE-0022: Implement parsing, AST, and semantic analysis for #selector.

This commit is contained in:
Doug Gregor
2016-01-26 13:35:21 -08:00
parent b2290992fa
commit dccf3155f1
20 changed files with 421 additions and 11 deletions

View File

@@ -1016,6 +1016,14 @@ ERROR(expected_type_after_as,none,
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
//------------------------------------------------------------------------------

View File

@@ -88,6 +88,10 @@ ERROR(unsupported_c_function_pointer_conversion,none,
"C function pointer signature %0 is not compatible with expected type %1",
(Type, Type))
ERROR(unsupported_objc_selector,none,
"code generation for #selector is unsupported",
())
// Definite initialization diagnostics.
NOTE(variable_defined_here,none,
"%select{variable|constant}0 defined here", (bool))

View File

@@ -353,7 +353,24 @@ ERROR(noescape_functiontype_mismatch,none,
"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))
ERROR(cannot_return_value_from_void_func,none,
"unexpected non-void return value in void function", ())

View File

@@ -3695,6 +3695,44 @@ public:
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

View File

@@ -155,6 +155,7 @@ EXPR(DefaultValue, Expr)
EXPR(CodeCompletion, Expr)
UNCHECKED_EXPR(UnresolvedPattern, Expr)
EXPR(EditorPlaceholder, Expr)
EXPR(ObjCSelector, Expr)
#undef EXPR_RANGE
#undef UNCHECKED_EXPR

View File

@@ -51,6 +51,7 @@ IDENTIFIER_(OptionalNilComparisonType)
IDENTIFIER(Protocol)
IDENTIFIER(rawValue)
IDENTIFIER(RawValue)
IDENTIFIER(Selector)
IDENTIFIER(self)
IDENTIFIER(Self)
IDENTIFIER(setObject)

View File

@@ -1097,6 +1097,7 @@ public:
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();

View File

@@ -44,6 +44,7 @@ enum class tok {
pound_endif,
pound_line,
pound_available,
pound_selector,
comment,
#define KEYWORD(X) kw_ ## X,

View File

@@ -2170,6 +2170,16 @@ public:
}
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.

View File

@@ -803,6 +803,14 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
return E;
}
Expr *visitObjCSelectorExpr(ObjCSelectorExpr *E) {
Expr *sub = doIt(E->getSubExpr());
if (!sub) return nullptr;
E->setSubExpr(sub);
return E;
}
//===--------------------------------------------------------------------===//
// Everything Else
//===--------------------------------------------------------------------===//

View File

@@ -314,6 +314,7 @@ void Expr::propagateLValueAccessKind(AccessKind accessKind,
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 @@ bool Expr::canAppendCallParentheses() const {
case ExprKind::StringLiteral:
case ExprKind::InterpolatedStringLiteral:
case ExprKind::MagicIdentifierLiteral:
case ExprKind::ObjCSelector:
return true;
case ExprKind::ObjectLiteral:

View File

@@ -98,6 +98,7 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile)
#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;

View File

@@ -1619,6 +1619,11 @@ Restart:
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--;

View File

@@ -434,6 +434,7 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
/// expr-postfix(Mode)
/// operator-prefix expr-unary(Mode)
/// '&' expr-unary(Mode)
/// expr-selector
///
ParserResult<Expr> Parser::parseExprUnary(Diag<> Message, bool isExprBasic) {
UnresolvedDeclRefExpr *Operator;
@@ -454,6 +455,9 @@ ParserResult<Expr> Parser::parseExprUnary(Diag<> Message, bool isExprBasic) {
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
@@ -499,6 +503,50 @@ ParserResult<Expr> Parser::parseExprUnary(Diag<> Message, bool isExprBasic) {
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:

View File

@@ -197,6 +197,7 @@ namespace {
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);
@@ -1942,6 +1943,13 @@ visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, SGFContext C) {
return visit(E->getSemanticExpr(), C);
}
RValue RValueEmitter::visitObjCSelectorExpr(ObjCSelectorExpr *e, SGFContext C) {
SILType loweredSelectorTy = SGF.getLoweredType(e->getType());
SGF.SGM.diagnose(e, diag::unsupported_objc_selector);
return RValue(SGF, e, SGF.emitUndef(e, loweredSelectorTy));
}
static StringRef
getMagicFunctionString(SILGenFunction &gen) {
assert(gen.MagicFunctionName

View File

@@ -3242,6 +3242,151 @@ namespace {
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)) {
if (force->isImplicit()) {
subExpr = force->getSubExpr();
continue;
}
break;
}
// 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);

View File

@@ -2738,6 +2738,26 @@ namespace {
// 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

View File

@@ -111,11 +111,11 @@ Type TypeChecker::getExceptionType(DeclContext *dc, SourceLoc loc) {
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;
@@ -142,17 +142,24 @@ static Type getObjectiveCClassType(TypeChecker &TC,
}
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) {

View File

@@ -515,6 +515,7 @@ private:
Type UInt8Type;
Type NSObjectType;
Type NSErrorType;
Type ObjCSelectorType;
Type ExceptionType;
/// The \c Swift.UnsafeMutablePointer<T> declaration.
@@ -589,6 +590,7 @@ public:
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

View File

@@ -0,0 +1,83 @@
// 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 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 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) {
// 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:))
// 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) // 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}}
}