Skip to content

Fix recent regression with pattern binding capture checking #26529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/Sema/DebuggerTestingTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class DebuggerTestingTransform : public ASTWalker {

// Captures have to be computed after the closure is type-checked. This
// ensures that the type checker can infer <noescape> for captured values.
TC.computeCaptures(Closure);
TypeChecker::computeCaptures(Closure);

return {false, FinalExpr};
}
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2135,6 +2135,7 @@ class VarDeclUsageChecker : public ASTWalker {
// If this is a nested function with a capture list, mark any captured
// variables.
if (afd->isBodyTypeChecked()) {
TypeChecker::computeCaptures(afd);
for (const auto &capture : afd->getCaptureInfo().getCaptures())
addMark(capture.getDecl(), RK_Read|RK_Written);
} else {
Expand Down
45 changes: 24 additions & 21 deletions lib/Sema/TypeCheckCaptures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ using namespace swift;
namespace {

class FindCapturedVars : public ASTWalker {
TypeChecker &TC;
ASTContext &Context;
SmallVector<CapturedValue, 4> Captures;
llvm::SmallDenseMap<ValueDecl*, unsigned, 4> captureEntryNumber;
SourceLoc GenericParamCaptureLoc;
Expand All @@ -44,12 +44,12 @@ class FindCapturedVars : public ASTWalker {
bool NoEscape, ObjC;

public:
FindCapturedVars(TypeChecker &tc,
FindCapturedVars(ASTContext &Context,
SourceLoc CaptureLoc,
DeclContext *CurDC,
bool NoEscape,
bool ObjC)
: TC(tc), CaptureLoc(CaptureLoc), CurDC(CurDC),
: Context(Context), CaptureLoc(CaptureLoc), CurDC(CurDC),
NoEscape(NoEscape), ObjC(ObjC) {}

CaptureInfo getCaptureInfo() const {
Expand All @@ -71,7 +71,7 @@ class FindCapturedVars : public ASTWalker {
if (Captures.empty())
result.setCaptures(None);
else
result.setCaptures(TC.Context.AllocateCopy(Captures));
result.setCaptures(Context.AllocateCopy(Captures));

return result;
}
Expand Down Expand Up @@ -219,7 +219,7 @@ class FindCapturedVars : public ASTWalker {
// can safely ignore them.
// FIXME(TapExpr): This is probably caused by the scoping
// algorithm's ignorance of TapExpr. We should fix that.
if (D->getBaseName() == D->getASTContext().Id_dollarInterpolation)
if (D->getBaseName() == Context.Id_dollarInterpolation)
return { false, DRE };

// Capture the generic parameters of the decl, unless it's a
Expand Down Expand Up @@ -273,14 +273,14 @@ class FindCapturedVars : public ASTWalker {
// This is not supported since nominal types cannot capture values.
if (auto NTD = dyn_cast<NominalTypeDecl>(TmpDC)) {
if (DC->isLocalContext()) {
TC.diagnose(DRE->getLoc(), diag::capture_across_type_decl,
NTD->getDescriptiveKind(),
D->getBaseName().getIdentifier());
Context.Diags.diagnose(DRE->getLoc(), diag::capture_across_type_decl,
NTD->getDescriptiveKind(),
D->getBaseName().getIdentifier());

TC.diagnose(NTD->getLoc(), diag::kind_declared_here,
DescriptiveDeclKind::Type);
NTD->diagnose(diag::kind_declared_here,
DescriptiveDeclKind::Type);

TC.diagnose(D, diag::decl_declared_here, D->getFullName());
D->diagnose(diag::decl_declared_here, D->getFullName());
return { false, DRE };
}
}
Expand Down Expand Up @@ -328,7 +328,7 @@ class FindCapturedVars : public ASTWalker {
}

void propagateCaptures(AnyFunctionRef innerClosure, SourceLoc captureLoc) {
TC.computeCaptures(innerClosure);
TypeChecker::computeCaptures(innerClosure);

auto &captureInfo = innerClosure.getCaptureInfo();

Expand Down Expand Up @@ -597,7 +597,8 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
if (!AFR.getBody())
return;

FindCapturedVars finder(*this,
auto &Context = AFR.getAsDeclContext()->getASTContext();
FindCapturedVars finder(Context,
AFR.getLoc(),
AFR.getAsDeclContext(),
AFR.isKnownNoEscape(),
Expand All @@ -624,27 +625,29 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
if (AFD && finder.getGenericParamCaptureLoc().isValid()) {
if (auto Clas = AFD->getParent()->getSelfClassDecl()) {
if (Clas->usesObjCGenericsModel()) {
diagnose(AFD->getLoc(),
diag::objc_generic_extension_using_type_parameter);
AFD->diagnose(diag::objc_generic_extension_using_type_parameter);

// If it's possible, suggest adding @objc.
Optional<ForeignErrorConvention> errorConvention;
if (!AFD->isObjC() &&
isRepresentableInObjC(AFD, ObjCReason::MemberOfObjCMembersClass,
errorConvention)) {
diagnose(AFD->getLoc(),
AFD->diagnose(
diag::objc_generic_extension_using_type_parameter_try_objc)
.fixItInsert(AFD->getAttributeInsertionLoc(false), "@objc ");
}

diagnose(finder.getGenericParamCaptureLoc(),
diag::objc_generic_extension_using_type_parameter_here);
Context.Diags.diagnose(
finder.getGenericParamCaptureLoc(),
diag::objc_generic_extension_using_type_parameter_here);
}
}
}
}

void TypeChecker::checkPatternBindingCaptures(NominalTypeDecl *typeDecl) {
auto &ctx = typeDecl->getASTContext();

for (auto member : typeDecl->getMembers()) {
// Ignore everything other than PBDs.
auto *PBD = dyn_cast<PatternBindingDecl>(member);
Expand All @@ -659,16 +662,16 @@ void TypeChecker::checkPatternBindingCaptures(NominalTypeDecl *typeDecl) {
if (init == nullptr)
continue;

FindCapturedVars finder(*this,
FindCapturedVars finder(ctx,
init->getLoc(),
PBD->getInitContext(i),
/*NoEscape=*/false,
/*ObjC=*/false);
init->walk(finder);

if (finder.getDynamicSelfCaptureLoc().isValid()) {
diagnose(finder.getDynamicSelfCaptureLoc(),
diag::dynamic_self_stored_property_init);
ctx.Diags.diagnose(finder.getDynamicSelfCaptureLoc(),
diag::dynamic_self_stored_property_init);
}

auto captures = finder.getCaptureInfo();
Expand Down
22 changes: 13 additions & 9 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2248,14 +2248,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
}
}

// FIXME: Temporary hack until capture computation has been request-ified.
if (VD->getDeclContext()->isLocalContext()) {
VD->visitOpaqueAccessors([&](AccessorDecl *accessor) {
if (accessor->isImplicit())
TC.definedFunctions.push_back(accessor);
});
}

// Under the Swift 3 inference rules, if we have @IBInspectable or
// @GKInspectable but did not infer @objc, warn that the attribute is
if (!VD->isObjC() && TC.Context.LangOpts.EnableSwift3ObjCInference) {
Expand Down Expand Up @@ -3070,6 +3062,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
if (requiresDefinition(FD) && !FD->hasBody()) {
// Complain if we should have a body.
TC.diagnose(FD->getLoc(), diag::func_decl_without_brace);
} else if (FD->getDeclContext()->isLocalContext()) {
// Check local function bodies right away.
TC.typeCheckAbstractFunctionBody(FD);
} else {
// Record the body.
TC.definedFunctions.push_back(FD);
Expand Down Expand Up @@ -3291,6 +3286,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
if (requiresDefinition(CD) && !CD->hasBody()) {
// Complain if we should have a body.
TC.diagnose(CD->getLoc(), diag::missing_initializer_def);
} else if (CD->getDeclContext()->isLocalContext()) {
// Check local function bodies right away.
TC.typeCheckAbstractFunctionBody(CD);
} else {
TC.definedFunctions.push_back(CD);
}
Expand All @@ -3303,7 +3301,13 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
void visitDestructorDecl(DestructorDecl *DD) {
TC.validateDecl(DD);
TC.checkDeclAttributes(DD);
TC.definedFunctions.push_back(DD);

if (DD->getDeclContext()->isLocalContext()) {
// Check local function bodies right away.
TC.typeCheckAbstractFunctionBody(DD);
} else {
TC.definedFunctions.push_back(DD);
}
}
};
} // end anonymous namespace
Expand Down
4 changes: 3 additions & 1 deletion lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1926,7 +1926,9 @@ static Type getFunctionBuilderType(FuncDecl *FD) {
}

bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) {
return typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc());
auto result = typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc());
checkFunctionErrorHandling(AFD);
return result;
}

static Expr* constructCallToSuperInit(ConstructorDecl *ctor,
Expand Down
12 changes: 3 additions & 9 deletions lib/Sema/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC)
for (unsigned n = TC.definedFunctions.size(); currentFunctionIdx != n;
++currentFunctionIdx) {
auto *AFD = TC.definedFunctions[currentFunctionIdx];
assert(!AFD->getDeclContext()->isLocalContext());

TC.typeCheckAbstractFunctionBody(AFD);
}
Expand Down Expand Up @@ -358,19 +359,12 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC)

// Compute captures for functions and closures we visited.
for (auto *closure : TC.ClosuresWithUncomputedCaptures) {
TC.computeCaptures(closure);
TypeChecker::computeCaptures(closure);
}
TC.ClosuresWithUncomputedCaptures.clear();

for (AbstractFunctionDecl *FD : reversed(TC.definedFunctions)) {
TC.computeCaptures(FD);
}

// Check error-handling correctness for all the functions defined in
// this file. This can depend on all of their interior function
// bodies having been type-checked.
for (AbstractFunctionDecl *FD : TC.definedFunctions) {
TC.checkFunctionErrorHandling(FD);
TypeChecker::computeCaptures(FD);
}

TC.definedFunctions.clear();
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -1430,10 +1430,10 @@ class TypeChecker final : public LazyResolver {
bool typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt);

/// Compute the set of captures for the given function or closure.
void computeCaptures(AnyFunctionRef AFR);
static void computeCaptures(AnyFunctionRef AFR);

/// Check for invalid captures from stored property initializers.
void checkPatternBindingCaptures(NominalTypeDecl *typeDecl);
static void checkPatternBindingCaptures(NominalTypeDecl *typeDecl);

/// Change the context of closures in the given initializer
/// expression to the given context.
Expand Down
1 change: 1 addition & 0 deletions test/NameBinding/name-binding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func overloadtest(x: Int) {
func localtest() {
func shadowbug() {
var Foo = 10
// expected-warning@-1 {{initialization of variable 'Foo' was never used; consider replacing with assignment to '_' or removing it}}
func g() {
struct S {
// FIXME: Swap these two lines to crash our broken lookup.
Expand Down
5 changes: 3 additions & 2 deletions test/NameBinding/name_lookup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -410,13 +410,14 @@ extension ThisDerived1 {

// <rdar://problem/11554141>
func shadowbug() {
var Foo = 10
let Foo = 10
func g() {
struct S {
var x : Foo
typealias Foo = Int
}
}
_ = Foo
}
func scopebug() {
let Foo = 10
Expand Down Expand Up @@ -633,4 +634,4 @@ struct PatternBindingWithTwoVars3 { var x = y, y = x }
func sr9015() {
let closure1 = { closure2() } // expected-error {{let 'closure1' references itself}}
let closure2 = { closure1() }
}
}
1 change: 1 addition & 0 deletions test/Parse/dollar_identifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func escapedDollarAnd() {

func $declareWithDollar() { // expected-error{{cannot declare entity named '$declareWithDollar'}}
var $foo = 17 // expected-error{{cannot declare entity named '$foo'}}
// expected-warning@-1 {{initialization of variable '$foo' was never used; consider replacing with assignment to '_' or removing it}}
func $bar() { } // expected-error{{cannot declare entity named '$bar'}}
func wibble(
$a: Int, // expected-error{{cannot declare entity named '$a'}}
Expand Down
2 changes: 2 additions & 0 deletions test/Parse/init_deinit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,13 @@ func fooFunc() {

func barFunc() {
var x : () = { () -> () in
// expected-warning@-1 {{variable 'x' was never used; consider replacing with '_' or removing it}}
init() {} // expected-error {{initializers may only be declared within a type}}
return
} ()

var y : () = { () -> () in
// expected-warning@-1 {{variable 'y' was never used; consider replacing with '_' or removing it}}
deinit {} // expected-error {{deinitializers may only be declared within a class}}
return
} ()
Expand Down
3 changes: 2 additions & 1 deletion test/SILGen/capture_order.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ class SR4812 {
let bar = { [weak self] in
// expected-error@-1 {{closure captures 'bar' before it is declared}}
// expected-note@-2 {{captured value declared here}}
// expected-warning@-3 {{variable 'self' was written to, but never read}}
bar2()
}
func bar2() {
Expand Down Expand Up @@ -198,4 +199,4 @@ class rdar40600800 {
}
}
}
}
}
2 changes: 1 addition & 1 deletion test/SILGen/inlinable_attribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public func bas() {
}

// CHECK-LABEL: sil shared [serialized] [ossa] @$s19inlinable_attribute3bas{{[_0-9a-zA-Z]*}}U_
let zung = {
let _ = {
// CHECK-LABEL: sil shared [serializable] [ossa] @$s19inlinable_attribute3basyyFyycfU_7zippityL_yyF
func zippity() { }
}
Expand Down
17 changes: 17 additions & 0 deletions test/SILGen/lazy_properties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,20 @@ class LazyClass {
// CHECK-LABEL: sil hidden [ossa] @$s15lazy_properties9LazyClassC1xSivs : $@convention(method) (Int, @guaranteed LazyClass) -> ()
// CHECK: ref_element_addr %1 : $LazyClass, #LazyClass.$__lazy_storage_$_x
// CHECK: return

// rdar://problem/53956342
class Butt {
func foo() -> Int { return 0 }

lazy var butt: Int = {
func bar() -> Int{
return foo()
}
return bar()
}()
}

// Both the closure and the local function inside of it should capture 'self':

// CHECK-LABEL: sil private [ossa] @$s15lazy_properties4ButtC4buttSivgSiyXEfU_ : $@convention(thin) (@guaranteed Butt) -> Int
// CHECK-LABEL: sil private [ossa] @$s15lazy_properties4ButtC4buttSivgSiyXEfU_3barL_SiyF : $@convention(thin) (@guaranteed Butt) -> Int
6 changes: 4 additions & 2 deletions test/Sema/availability_versions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -482,13 +482,15 @@ func accessUnavailableProperties(_ o: ClassWithUnavailableProperties) {

// Nesting in source of assignment
var v: Int

v = o.propWithGetterOnlyAvailableOn10_51 // expected-error {{getter for 'propWithGetterOnlyAvailableOn10_51' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{add 'if #available' version check}}

v = (o.propWithGetterOnlyAvailableOn10_51) // expected-error {{getter for 'propWithGetterOnlyAvailableOn10_51' is only available in macOS 10.51 or newer}}
// expected-note@-1 {{add 'if #available' version check}}


_ = v // muffle warning

// Inout requires access to both getter and setter

func takesInout(_ i : inout Int) { }
Expand Down
2 changes: 1 addition & 1 deletion test/Sema/diag_defer_block_end.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ if (x > y) {
func sr7307(_ value: Bool) {
let negated = !value
defer { // expected-warning {{'defer' statement at end of scope always executes immediately}}{{5-10=do}}
print("negated value is {negated}")
print("negated value is \(negated)")
}
}

Expand Down
Loading