mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Sema: Accept if #_hasSymbol() conditions in closure contexts.
Resolves rdar://100129165
This commit is contained in:
@@ -1394,8 +1394,17 @@ public:
|
|||||||
for (auto &elt : C) {
|
for (auto &elt : C) {
|
||||||
switch (elt.getKind()) {
|
switch (elt.getKind()) {
|
||||||
case StmtConditionElement::CK_Availability:
|
case StmtConditionElement::CK_Availability:
|
||||||
case StmtConditionElement::CK_HasSymbol:
|
|
||||||
break;
|
break;
|
||||||
|
case StmtConditionElement::CK_HasSymbol: {
|
||||||
|
auto E = elt.getHasSymbolInfo()->getSymbolExpr();
|
||||||
|
if (!E)
|
||||||
|
return true;
|
||||||
|
E = doIt(E);
|
||||||
|
if (!E)
|
||||||
|
return true;
|
||||||
|
elt.getHasSymbolInfo()->setSymbolExpr(E);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case StmtConditionElement::CK_Boolean: {
|
case StmtConditionElement::CK_Boolean: {
|
||||||
auto E = elt.getBoolean();
|
auto E = elt.getBoolean();
|
||||||
// Walk an expression condition normally.
|
// Walk an expression condition normally.
|
||||||
|
|||||||
@@ -8923,9 +8923,23 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
|
|||||||
for (auto &condElement : *stmtCondition) {
|
for (auto &condElement : *stmtCondition) {
|
||||||
switch (condElement.getKind()) {
|
switch (condElement.getKind()) {
|
||||||
case StmtConditionElement::CK_Availability:
|
case StmtConditionElement::CK_Availability:
|
||||||
case StmtConditionElement::CK_HasSymbol:
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
case StmtConditionElement::CK_HasSymbol: {
|
||||||
|
ConstraintSystem &cs = solution.getConstraintSystem();
|
||||||
|
auto target = *cs.getSolutionApplicationTarget(&condElement);
|
||||||
|
auto resolvedTarget = rewriteTarget(target);
|
||||||
|
if (!resolvedTarget)
|
||||||
|
return None;
|
||||||
|
|
||||||
|
auto info = condElement.getHasSymbolInfo();
|
||||||
|
auto rewrittenExpr = resolvedTarget->getAsExpr();
|
||||||
|
info->setSymbolExpr(rewrittenExpr);
|
||||||
|
info->setReferencedDecl(
|
||||||
|
TypeChecker::getReferencedDeclForHasSymbolCondition(rewrittenExpr));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
case StmtConditionElement::CK_Boolean: {
|
case StmtConditionElement::CK_Boolean: {
|
||||||
auto condExpr = condElement.getBoolean();
|
auto condExpr = condElement.getBoolean();
|
||||||
auto finalCondExpr = condExpr->walk(*this);
|
auto finalCondExpr = condExpr->walk(*this);
|
||||||
|
|||||||
@@ -4369,10 +4369,15 @@ bool ConstraintSystem::generateConstraints(StmtCondition condition,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
case StmtConditionElement::CK_HasSymbol: {
|
case StmtConditionElement::CK_HasSymbol: {
|
||||||
ASTContext &ctx = getASTContext();
|
Expr *symbolExpr = condElement.getHasSymbolInfo()->getSymbolExpr();
|
||||||
ctx.Diags.diagnose(condElement.getStartLoc(),
|
auto target = SolutionApplicationTarget(symbolExpr, dc, CTP_Unused,
|
||||||
diag::has_symbol_unsupported_in_closures);
|
Type(), /*isDiscarded=*/false);
|
||||||
return true;
|
|
||||||
|
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
setSolutionApplicationTarget(&condElement, target);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
case StmtConditionElement::CK_Boolean: {
|
case StmtConditionElement::CK_Boolean: {
|
||||||
|
|||||||
@@ -4302,6 +4302,50 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform MiscDiagnostics for the conditions belonging to a \c
|
||||||
|
/// LabeledConditionalStmt.
|
||||||
|
static void checkLabeledStmtConditions(ASTContext &ctx,
|
||||||
|
const LabeledConditionalStmt *stmt,
|
||||||
|
DeclContext *DC) {
|
||||||
|
for (auto elt : stmt->getCond()) {
|
||||||
|
// Check for implicit optional promotions in stmt-condition patterns.
|
||||||
|
checkImplicitPromotionsInCondition(elt, ctx);
|
||||||
|
|
||||||
|
switch (elt.getKind()) {
|
||||||
|
case StmtConditionElement::CK_Boolean:
|
||||||
|
case StmtConditionElement::CK_PatternBinding:
|
||||||
|
case StmtConditionElement::CK_Availability:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case StmtConditionElement::CK_HasSymbol: {
|
||||||
|
auto info = elt.getHasSymbolInfo();
|
||||||
|
auto symbolExpr = info->getSymbolExpr();
|
||||||
|
if (!symbolExpr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!symbolExpr->getType())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (auto decl = info->getReferencedDecl().getDecl()) {
|
||||||
|
// `if #_hasSymbol(someStronglyLinkedSymbol)` is functionally a no-op
|
||||||
|
// and may indicate the developer has mis-identified the declaration
|
||||||
|
// they want to check (or forgot to import the module weakly).
|
||||||
|
if (!decl->isWeakImported(DC->getParentModule())) {
|
||||||
|
ctx.Diags.diagnose(symbolExpr->getLoc(),
|
||||||
|
diag::has_symbol_decl_must_be_weak,
|
||||||
|
decl->getDescriptiveKind(), decl->getName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Diagnose because we weren't able to interpret the expression as one
|
||||||
|
// that uniquely identifies a single declaration.
|
||||||
|
ctx.Diags.diagnose(symbolExpr->getLoc(), diag::has_symbol_invalid_expr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void diagnoseUnintendedOptionalBehavior(const Expr *E,
|
static void diagnoseUnintendedOptionalBehavior(const Expr *E,
|
||||||
const DeclContext *DC) {
|
const DeclContext *DC) {
|
||||||
if (!E || isa<ErrorExpr>(E) || !E->getType())
|
if (!E || isa<ErrorExpr>(E) || !E->getType())
|
||||||
@@ -5290,11 +5334,9 @@ void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {
|
|||||||
checkSwitch(ctx, switchStmt, DC);
|
checkSwitch(ctx, switchStmt, DC);
|
||||||
|
|
||||||
checkStmtConditionTrailingClosure(ctx, S);
|
checkStmtConditionTrailingClosure(ctx, S);
|
||||||
|
|
||||||
// Check for implicit optional promotions in stmt-condition patterns.
|
|
||||||
if (auto *lcs = dyn_cast<LabeledConditionalStmt>(S))
|
if (auto *lcs = dyn_cast<LabeledConditionalStmt>(S))
|
||||||
for (const auto &elt : lcs->getCond())
|
checkLabeledStmtConditions(ctx, lcs, DC);
|
||||||
checkImplicitPromotionsInCondition(elt, ctx);
|
|
||||||
|
|
||||||
if (!ctx.LangOpts.DisableAvailabilityChecking)
|
if (!ctx.LangOpts.DisableAvailabilityChecking)
|
||||||
diagnoseStmtAvailability(S, const_cast<DeclContext*>(DC));
|
diagnoseStmtAvailability(S, const_cast<DeclContext*>(DC));
|
||||||
|
|||||||
@@ -448,8 +448,7 @@ static Expr *getDeclRefProvidingExpressionForHasSymbol(Expr *E) {
|
|||||||
return E;
|
return E;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ConcreteDeclRef
|
ConcreteDeclRef TypeChecker::getReferencedDeclForHasSymbolCondition(Expr *E) {
|
||||||
getReferencedDeclForHasSymbolCondition(ASTContext &Context, Expr *E) {
|
|
||||||
// Match DotSelfExprs (e.g. `SomeStruct.self`) when the type is static.
|
// Match DotSelfExprs (e.g. `SomeStruct.self`) when the type is static.
|
||||||
if (auto DSE = dyn_cast<DotSelfExpr>(E)) {
|
if (auto DSE = dyn_cast<DotSelfExpr>(E)) {
|
||||||
if (DSE->isStaticallyDerivedMetatype())
|
if (DSE->isStaticallyDerivedMetatype())
|
||||||
@@ -461,7 +460,6 @@ getReferencedDeclForHasSymbolCondition(ASTContext &Context, Expr *E) {
|
|||||||
return CDR;
|
return CDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.Diags.diagnose(E->getLoc(), diag::has_symbol_invalid_expr);
|
|
||||||
return ConcreteDeclRef();
|
return ConcreteDeclRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,20 +504,10 @@ bool TypeChecker::typeCheckStmtConditionElement(StmtConditionElement &elt,
|
|||||||
auto exprTy = TypeChecker::typeCheckExpression(E, dc);
|
auto exprTy = TypeChecker::typeCheckExpression(E, dc);
|
||||||
Info->setSymbolExpr(E);
|
Info->setSymbolExpr(E);
|
||||||
|
|
||||||
if (!exprTy)
|
if (exprTy)
|
||||||
return true;
|
Info->setReferencedDecl(getReferencedDeclForHasSymbolCondition(E));
|
||||||
|
|
||||||
auto CDR = getReferencedDeclForHasSymbolCondition(Context, E);
|
return !exprTy;
|
||||||
if (!CDR)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
auto decl = CDR.getDecl();
|
|
||||||
if (!decl->isWeakImported(dc->getParentModule())) {
|
|
||||||
Context.Diags.diagnose(E->getLoc(), diag::has_symbol_decl_must_be_weak,
|
|
||||||
decl->getDescriptiveKind(), decl->getName());
|
|
||||||
}
|
|
||||||
Info->setReferencedDecl(CDR);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto E = elt.getBooleanOrNull()) {
|
if (auto E = elt.getBooleanOrNull()) {
|
||||||
|
|||||||
@@ -441,6 +441,10 @@ Expr *substituteInputSugarTypeForResult(ApplyExpr *E);
|
|||||||
bool typeCheckStmtConditionElement(StmtConditionElement &elt, bool &isFalsable,
|
bool typeCheckStmtConditionElement(StmtConditionElement &elt, bool &isFalsable,
|
||||||
DeclContext *dc);
|
DeclContext *dc);
|
||||||
|
|
||||||
|
/// Returns the unique decl ref identified by the expr according to the
|
||||||
|
/// requirements of the \c #_hasSymbol() condition type.
|
||||||
|
ConcreteDeclRef getReferencedDeclForHasSymbolCondition(Expr *E);
|
||||||
|
|
||||||
void typeCheckASTNode(ASTNode &node, DeclContext *DC,
|
void typeCheckASTNode(ASTNode &node, DeclContext *DC,
|
||||||
bool LeaveBodyUnchecked = false);
|
bool LeaveBodyUnchecked = false);
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ func testNotWeakDeclDiagnostics(_ s: LocalStruct) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testInvalidExpressionsDiagnostics() {
|
func testInvalidExpressionsDiagnostics() {
|
||||||
|
if #_hasSymbol(unknownDecl) {} // expected-error {{cannot find 'unknownDecl' in scope}}
|
||||||
if #_hasSymbol(noArgFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
if #_hasSymbol(noArgFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
||||||
if #_hasSymbol(global - 1) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
if #_hasSymbol(global - 1) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
||||||
if #_hasSymbol(S.staticFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
if #_hasSymbol(S.staticFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
||||||
@@ -101,15 +102,36 @@ func testInvalidExpressionsDiagnostics() {
|
|||||||
if #_hasSymbol(1 as S) {} // expected-error {{cannot convert value of type 'Int' to type 'S' in coercion}}
|
if #_hasSymbol(1 as S) {} // expected-error {{cannot convert value of type 'Int' to type 'S' in coercion}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMultiStatementClosure() {
|
func testGuard() {
|
||||||
let _: () -> Void = { // expected-error {{unable to infer closure type in the current context}}
|
guard #_hasSymbol(global) else { return }
|
||||||
if #_hasSymbol(global) {} // expected-error 2 {{#_hasSymbol is not supported in closures}}
|
guard #_hasSymbol(unknownDecl) else { return } // expected-error {{cannot find 'unknownDecl' in scope}}
|
||||||
}
|
guard #_hasSymbol(localFunc) else { return } // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
|
||||||
|
}
|
||||||
let _: () -> Void = { // expected-error {{unable to infer closure type in the current context}}
|
|
||||||
if #_hasSymbol(global) {} // expected-error 2 {{#_hasSymbol is not supported in closures}}
|
func testWhile() {
|
||||||
localFunc()
|
while #_hasSymbol(global) { break }
|
||||||
}
|
while #_hasSymbol(unknownDecl) { break } // expected-error {{cannot find 'unknownDecl' in scope}}
|
||||||
|
while #_hasSymbol(localFunc) { break } // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doIt(_ closure: () -> ()) {
|
||||||
|
closure()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClosure() {
|
||||||
|
doIt { if #_hasSymbol(global) {} }
|
||||||
|
doIt { if #_hasSymbol(noArgFunc) {} }
|
||||||
|
doIt { if #_hasSymbol(ambiguousFunc as () -> Int) {} }
|
||||||
|
doIt { if #_hasSymbol(S.self) {} }
|
||||||
|
doIt { if #_hasSymbol(ambiguousFunc) {} } // expected-error {{ambiguous use of 'ambiguousFunc()'}}
|
||||||
|
doIt { if #_hasSymbol(localFunc) {} } // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
|
||||||
|
doIt { if #_hasSymbol(unknownDecl) {} } // expected-error {{cannot find 'unknownDecl' in scope}}
|
||||||
|
doIt { if #_hasSymbol(noArgFunc()) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
||||||
|
doIt { if #_hasSymbol(global - 1) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
||||||
|
doIt { if #_hasSymbol(S.staticFunc()) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
||||||
|
doIt { if #_hasSymbol(C.classFunc()) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
||||||
|
doIt { if #_hasSymbol(1 as Int) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
|
||||||
|
doIt { if #_hasSymbol(1 as S) {} } // expected-error {{cannot convert value of type 'Int' to type 'S' in coercion}}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol View {}
|
protocol View {}
|
||||||
@@ -120,15 +142,23 @@ protocol View {}
|
|||||||
static func buildEither<Content>(second content: Content) -> Content where Content : View { fatalError() }
|
static func buildEither<Content>(second content: Content) -> Content where Content : View { fatalError() }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Image : View {
|
struct Image : View {}
|
||||||
}
|
|
||||||
|
|
||||||
struct MyView {
|
struct MyView {
|
||||||
@ViewBuilder var body: some View {
|
let image = Image()
|
||||||
if #_hasSymbol(global) { // expected-error {{#_hasSymbol is not supported in closures}}
|
|
||||||
Image()
|
@ViewBuilder var globalView: some View {
|
||||||
} else {
|
if #_hasSymbol(global) { image }
|
||||||
Image()
|
else { image }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder var ambiguousFuncView: some View {
|
||||||
|
if #_hasSymbol(ambiguousFunc) { image } // expected-error {{ambiguous use of 'ambiguousFunc()'}}
|
||||||
|
else { image }
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder var localFuncView: some View {
|
||||||
|
if #_hasSymbol(localFunc) { image } // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
|
||||||
|
else { image }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user