In addition to the cases you mentioned, you can call try at 
top-level code. Here is a simple self-contained example:
// main.swift:
enum MyError : Error {
    case failed
}
func foo() throws -> Int   {
    throw MyError.failed
}
defer { print("Good bye.") }
let x = try foo()
print(x)
You can compile and run this as a Xcode "Command Line Project"
or directly from the command line:
$ swiftc main.swift
$ ./main
Good bye.
Fatal error: Error raised at top level: main.MyError.failed: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.74.1/src/swift/stdlib/public/core/ErrorType.swift, line 187
Illegal instruction: 4
The failed try in the top-level code causes the program to
terminate with an error message. Deferred statement (if present) will be executed however.
This is slightly different from using a forced try! statement,
which causes the program to abort as well, but immediately, without executing deferred statements. (This can be relevant if deferred
statements are used to clean-up resources, e.g. remove temporary files).
The error message originates from ErrorType.swift, line 187:
/// Invoked by the compiler when code at top level throws an uncaught error.
@_inlineable // FIXME(sil-serialize-all)
@_silgen_name("swift_errorInMain")
public func _errorInMain(_ error: Error) {
  fatalError("Error raised at top level: \(String(reflecting: error))")
}
(also observed in Non Exhaustive List When Handling Errors Inside a Class Function in Swift). 
Apparently the top-level code behaves as if embedded in a 
do-catch block:
do {
    func foo() throws -> Int   {
        throw NSError(domain: "foo", code: 0, userInfo: nil)
    }
    defer { print("Good bye.") }
    let x = try foo()
} catch {
    fatalError("Error raised at top level: \(String(reflecting: error))")
}