Skip to content
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

[SR-8266] Compiler crash when checking exclusivity of inout alias #50797

Closed
palimondo mannequin opened this issue Jul 16, 2018 · 10 comments
Closed

[SR-8266] Compiler crash when checking exclusivity of inout alias #50797

palimondo mannequin opened this issue Jul 16, 2018 · 10 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software

Comments

@palimondo
Copy link
Mannequin

palimondo mannequin commented Jul 16, 2018

Previous ID SR-8266
Radar rdar://problem/42242406
Original Reporter @palimondo
Type Bug
Status Closed
Resolution Done

Attachment: Download

Environment

swift-DEVELOPMENT-SNAPSHOT-2018-07-12-a

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, CompilerCrash
Assignee @atrick
Priority Medium

md5: dc1f574d8884c55d3be4243ade084ea0

Issue Description:

While refactoring some legacy swift code, that included a peculiar workaround to avoid capturing mutable inout self reference when creating a closure passed filter method, I've stumbled upon a crash in one of the latest swift master builds (dev snapshot 2018-07-12). The particular closure is non-escaping, but it implicitly captures a mutable inout self, because it references two vars.

The old workaround was to create immutable copies of the vars and use those inside the closure: let (_tags, _skipTags) = (tags, skipTags)

I thought that current version of the compiler would be able to reason correctly about this code to not require the explicit copying...

The compiling attached file crashes with following error:

Unexpected partial_apply use:   br bb4(%129 : $@callee_guaranteed (@guaranteed BenchmarkInfo) -> Bool) // id: %130
A partial_apply with @inout_aliasable may only be used as a @noescape function type argument.
UNREACHABLE executed at /Users/mondo/Developer/swift-source/swift/lib/SILOptimizer/Mandatory/DiagnoseStaticExclusivity.cpp:1048!
0  swift                    0x0000000111b03268 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40
1  swift                    0x0000000111b021d6 llvm::sys::RunSignalHandlers() + 198
2  swift                    0x0000000111b038f2 SignalHandler(int) + 258
3  libsystem_platform.dylib 0x00007fff600ddf5a _sigtramp + 26
4  libsystem_platform.dylib 0x0000000119773250 _sigtramp + 3110687504
5  libsystem_c.dylib        0x00007fff5fe7b1ae abort + 127
6  swift                    0x0000000111a9ac3e llvm::llvm_unreachable_internal(char const*, char const*, unsigned int) + 542
7  swift                    0x000000010e9cb1df checkStaticExclusivity(swift::SILFunction&, swift::PostOrderFunctionInfo*, swift::AccessSummaryAnalysis*) + 10863
8  swift                    0x000000010e9e59e1 swift::SILPassManager::runPassOnFunction(unsigned int, swift::SILFunction*) + 1441
9  swift                    0x000000010e9e69c3 swift::SILPassManager::runFunctionPasses(unsigned int, unsigned int) + 1315
10 swift                    0x000000010e9e76d4 swift::SILPassManager::execute() + 644
11 swift                    0x000000010e15a7fb swift::SILPassManager::executePassPipelinePlan(swift::SILPassPipelinePlan const&) + 187
12 swift                    0x000000010e9ef784 swift::runSILDiagnosticPasses(swift::SILModule&) + 132
13 swift                    0x000000010dfed6f4 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 13060
14 swift                    0x000000010dfe9378 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 2936
15 swift                    0x000000010dfa324e main + 1134
16 libdyld.dylib            0x00007fff5fdcf015 start + 1

What's strange is that this code compile without error, when compiled with last Xcode Beta: Apple Swift version 4.2 (swiftlang-1000.0.25.1 clang-1000.10.28.1. I haven't tried running that, there might be some hidden failure, because when I don't use the nested funcs, but use inline closure syntax, the references to tags and skipTags trigger error: reference to property 'tags' in closure requires explicit 'self.' to make capture semantics explicit. But adding explicit self leads to error: closure cannot implicitly capture a mutating self parameter.

As the crash on latest master happens in exclusivity check, I thought it had something to do with having two references to self, one for tags and one for skipTags, but the crash happens even if one of them uses the immutable copy workaround.

@belkadan
Copy link
Contributor

@swift-ci create

@atrick
Copy link
Member

atrick commented Jul 25, 2018

The crash was fixed on 4.2:

<rdar://problem/42242406> [SR-8266]: Compiler crash when checking exclusivity of inout alias

The diagnostic will be strengthened on master shortly:

<rdar://problem/42560459> [Exclusivity] Failure to statically diagnose a conflict when passing conditional noescape closures.

@palimondo
Copy link
Mannequin Author

palimondo mannequin commented Aug 2, 2018

@atrick How does the resolution work in the end for the supplied testcase? Is the reference to mutable self legal here (I.e. is the immutable copy alias (let (_tags, _skipTags) = (tags, skipTags)) still necessary)?

@atrick
Copy link
Member

atrick commented Aug 2, 2018

@palimondo thanks for filing this and following through!

I'm able to write the test case code like this now without any crash or violation:

func byTags(b: BenchmarkInfo) -> Bool {
  return b.tags.isSuperset(of: tags) && b.tags.isDisjoint(with: skipTags)
} 

Can you point me directly to the code that has an exclusivity violation? You mentioned a closure in this bug description. If you point me to that, I'll explain why it's a violation.

@palimondo
Copy link
Mannequin Author

palimondo mannequin commented Aug 8, 2018

@atrick Thanks, the above case working is the best I was hoping for!

With inline closures, I was referring to the commented out filter block at the end of the attached test-case, below the comment line // Following are inline closure insread of nested function, the report different errors. (The typos therein are embarrassing… )

So, from my (shallow) understanding of the PR #18234, you did unify the implementation so that the exclusivity check works the same for inline closures and nested functions, therefore I'd expect those inline closures should now also work and not report errors?

@atrick
Copy link
Member

atrick commented Aug 16, 2018

@palimondo, thanks again! I'm still fixing two bugs in the exclusivity checker, and I now see there's a third bug in the type checker: https://bugs.swift.org/browse/SR-8546.

So, nested functions have always been modeled mostly like "inline" closures at each point they are used, but the type checker handles them differently. That means, assuming the type checker is happy, nested functiond can always be considered non-escaping whenever they're not passed to an explicitly `escaping` argument, meaning they can capture `inout` arguments.

(sorry this is complicated, I'm doing my best to explain)

Sadly, this is in no way properly supported, but it now accidentally works if you don't violate the closure escaping guarantee.

Specifically, the type checker "accidentally" correctly allows the nested function case. I say accidentally because it does not properly diagnose escaping violations. I did recently fix the exclusivity checker to handle this case so it at least detects exclusivity conflicts:

func doit(x: inout Int, _ f: ()->()) { f() }
public func outerFoo1(x: inout Int) {
  func innerFoo1() {
    x = 1
  }
  func innerFoo2() {
    x = 2
  }
  doit(x: &x, x == 0 ? innerFoo2 : innerFoo2)
}
error: overlapping accesses to 'x', but modification requires exclusive access; consider copying to a local variable
 doit(x: &x, x == 0 ? innerFoo2 : innerFoo2)

The type checker does not uniformly handle nested functions and inline closures. So it currently does not allow you to write the same code this way:

public func outerFoo2(x: inout Int) {
 doit(x: &x, x == 0 ? { x = 1 } : { x = 2})
}
error: escaping closures can only capture inout parameters explicitly by value
 doit(x: &x, x == 0 ? { x = 1 } : { x = 2})

If the type checker allowed it, then, yes, the exclusivity checker would uniformly handle both cases.

@palimondo
Copy link
Mannequin Author

palimondo mannequin commented Aug 16, 2018

Oh. So it looks like I've dug out some skeletons from the closet…
What are the future plans with regards to supporting the "accidentally" correctly working case of nested functions in the type checker? Do you leave it as is, or do you want to make that case illegal?

@palimondo
Copy link
Mannequin Author

palimondo mannequin commented Aug 16, 2018

From reading SR-8546 you want to make it illegal… correct?

@palimondo
Copy link
Mannequin Author

palimondo mannequin commented Aug 16, 2018

Ah, I think I get it now: that is illegal when passed to explicitly @escaping argument.

@atrick
Copy link
Member

atrick commented Aug 16, 2018

Right, the bug is a failure to diagnose a case that is clearly illegal.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added the crash Bug: A crash, i.e., an abnormal termination of software label Dec 12, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software
Projects
None yet
Development

No branches or pull requests

3 participants