Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

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: 2 additions & 0 deletions include/swift/AST/DiagnosticGroups.def
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ GROUP(no_group, "")

GROUP(ActorIsolatedCall, "actor-isolated-call")
GROUP(AvailabilityUnrecognizedName, "availability-unrecognized-name")
GROUP(BranchesHaveMismatchingTypes, "branches-have-mismatching-types")
GROUP(ClangDeclarationImport, "clang-declaration-import")
GROUP(ConformanceIsolation, "conformance-isolation")
GROUP(DeprecatedDeclaration, "deprecated-declaration")
Expand All @@ -66,6 +67,7 @@ GROUP(ResultBuilderMethods, "result-builder-methods")
GROUP(SendableClosureCaptures, "sendable-closure-captures")
GROUP(SendableMetatypes, "sendable-metatypes")
GROUP(SendingRisksDataRace, "sending-risks-data-race")
GROUP(StatementExpressionOutOfPlace, "statement-expression-out-of-place")
GROUP(StrictLanguageFeatures, "strict-language-features")
GROUP(StrictMemorySafety, "strict-memory-safety")
GROUP(StringInterpolationConformance, "string-interpolation-conformance")
Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1390,10 +1390,10 @@ ERROR(ternary_expr_cases_mismatch,none,
(Type, Type))

// Statements as expressions
ERROR(single_value_stmt_branches_mismatch,none,
GROUPED_ERROR(single_value_stmt_branches_mismatch,BranchesHaveMismatchingTypes,none,
"branches have mismatching types %0 and %1",
(Type, Type))
ERROR(single_value_stmt_out_of_place,none,
GROUPED_ERROR(single_value_stmt_out_of_place,StatementExpressionOutOfPlace,none,
"'%0' may only be used as expression in return, throw, or as the source "
"of an assignment",
(StmtKind))
Expand Down
88 changes: 88 additions & 0 deletions userdocs/diagnostics/branches-have-mismatching-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Branches have mismatching types

Result values in `if` and `switch` expressions must have the same type. Resolve this error by providing a type annotation or adjusting the values to have matching types.

The following code is invalid because one branch of the `if` expression produces a value of type `String`, and the other branch produces a value of type `Int`. The type of result can be either Int or String, but not both.

```swift
let result = if condition {
"hello, world!" // error: branches have mismatching types 'String' and 'Int'
} else {
42
}
```

The same restriction applies to each `case` of a `switch` statement used as an expression:

```swift
let result = switch myNumber {
case 0: "hello, world!" // error: branches have mismatching types 'String' and 'Int'
case 1: 42
default: "hello, again!"
}
```

These errors can be resolved by adjusting the code so every branch returns a value of the same type. For example, it would be valid for every branch to produce a `String`:

```swift
let result = if condition {
"hello, world!"
} else {
"goodbye!"
}
```

In other situations, it might be appropriate to use an `enum` or `protocol` type to represent the result. Both of these examples would also be valid:

```swift
enum MyEnum {
case message(String)
case number(Int)
}

let result = if condition {
MyEnum.string("hello, world!")
} else {
MyEnum.number(42)
}
```

```swift
protocol MyProtocol { ... }
extension String: MyProtocol { ... }
extension Int: MyProtocol { ... }

let result = if condition {
"hello, world!" as (any MyProtocol)
} else {
42 as (any MyProtocol)
}
```

Because each branch of an `if` or `switch` expression is typechecked independently, the following code is also invalid:

```swift
let result = if condition {
0 // error: branches have mismatching types 'Int' and 'Double'
} else {
1.0
}
```

Even though both `0` and `1.0` could represent a value of type `Double`, because each branch of the expression is typechecked independently `0` is inferred to be of type `Int` and `1.0` is inferred to be of type `Double`, causing a mismatch. In this case, an explicit type annotation can be used to ensure both literals are treated as a `Double`:

```swift
let result: Double = if condition {
0
} else {
1.0
}
```

```swift
let result = if condition {
0 as Double
} else {
1.0
}
```
2 changes: 2 additions & 0 deletions userdocs/diagnostics/diagnostic-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ presented specially within your IDE of choice. See below for the full list of th

- <doc:dynamic-callable-requirements>
- <doc:trailing-closure-matching>
- <doc:branches-have-mismatching-types>
- <doc:actor-isolated-call>
- <doc:sendable-closure-captures>
- <doc:string-interpolation-conformance>
Expand All @@ -33,6 +34,7 @@ presented specially within your IDE of choice. See below for the full list of th
- <doc:sendable-metatypes>
- <doc:sending-closure-risks-data-race>
- <doc:sending-risks-data-race>
- <doc:statement-expression-out-of-place>
- <doc:temporary-pointers>
- <doc:opaque-type-inference>
- <doc:mutable-global-variable>
Expand Down
53 changes: 53 additions & 0 deletions userdocs/diagnostics/statement-expression-out-of-place.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Statement may only be used as an expression in return, throw, or as the source of an assignment

Using an `if` or `switch` statement as an expression is only valid in certain contexts. Resolve this error by breaking up the expression or rewriting it into multiple statements.

An `if` or `switch` statement is allowed to be used as an expression:

* As the value of a return statement. For example:

```swift
func halveIfEven(value: Int) -> Int {
return if value % 2 == 0 {
value / 2
} else {
value
}
}
```

* As the value of a `throw` statement. For example:

```swift
func throwError(for errorCode: Int) throws {
throw switch errorCode {
case 1: MyError.foo
case 2: MyError.bar
default: MyError.default
}
}
```

* As the value assigned to a variable. For example:

```swift
let result = if condition {
"hello"
} else {
"goodbye"
}
```

In any other context, using `if` or `switch` as an expression is not allowed. For example, the following code is invalid because the`if` statement is used as as an operand of a binary operator expression:

```swift
// error: 'if' may only be used as expression in return, throw, or as the source of an assignment
let result = if condition1 { 1 } else { 2 } + 1
```

This error can be fixed by breaking the code up into multiple statements:

```swift
let x = if condition1 { 1 } else { 2 }
let result = x + 1
```