Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[stdlib] Allow a default for optional interpolations #80547

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 17 commits into from
May 7, 2025

Conversation

natecook1000
Copy link
Member

This adds an appendInterpolation overload to DefaultStringInterpolation that includes a parameter for providing a default string when the value to interpolate is nil. This allows this kind of usage:

let age: Int? = nil
print("Your age is \(age, default: "timeless")")
// Prints "Your age is timeless"

Includes an additional fixit when optional values are interpolated to use this default: parameter.

This adds an `appendInterpolation` overload to `DefaultStringInterpolation`
that includes a parameter for providing a default string when the value
to interpolate is `nil`. This allows this kind of usage:

    let age: Int? = nil
    print("Your age is \(age, default: "timeless")")
    // Prints "Your age is timeless"

Includes an additional fixit when optional values are interpolated to
use this `default:` parameter.
@natecook1000 natecook1000 added standard library Area: Standard library umbrella swift evolution proposal needed Flag → feature: A feature that warrants a Swift evolution proposal labels Apr 4, 2025
@@ -5688,6 +5688,11 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
.fixItInsertAfter(arg->getEndLoc(), ")");

if (kind == UnintendedInterpolationKind::Optional) {
// Suggest using a default interpolation value parameter.
Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_parameter)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@beccadax Do you know if it would be possible to only provide this fixit when the string interpolation type is DefaultStringInterpolation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you had the caller extract the ConcreteDeclRef and pass it in, then yes, that ought to be possible.

As an aside, I'm thinking we might not want to suggest both \(_:default:) and ??, as they mean nearly the same thing. Maybe ?? if it's an Optional<String> and \(_:default:) otherwise?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the logic so that the new fixit is only provided for DefaultStringInterpolation types, and isn't provided for optional strings, since it's the same functionality as the preferred ??. I left in the ?? suggestion for other optional types, since there could be some types or situations where a default value of that same type still makes sense.

(I also moved the String(describing:) fix-it to be the last suggestion, since it feels like "fix this warning" should be preferred over "silence this warning".)

@@ -5688,6 +5688,11 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
.fixItInsertAfter(arg->getEndLoc(), ")");

if (kind == UnintendedInterpolationKind::Optional) {
// Suggest using a default interpolation value parameter.
Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_parameter)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you had the caller extract the ConcreteDeclRef and pass it in, then yes, that ought to be possible.

As an aside, I'm thinking we might not want to suggest both \(_:default:) and ??, as they mean nearly the same thing. Maybe ?? if it's an Optional<String> and \(_:default:) otherwise?

}

@backDeployed(before: SwiftStdlib 6.2)
@_disfavoredOverload
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using @_disfavoredOverload for these, you should be able to provide a fourth T: TextOutputStreamable, T: CustomStringConvertible overload. That's how regular appendInterpolation(_:) works. (The type checker really penalizes disfavored overloads, so it's usually better to avoid it if you can.)

Also, consider making these @inlinable. When I originally implemented DefaultStringInterpolation, I did some interpolation performance benchmarking (the test should still be in the benchmark suite) and found that inlining really matters for it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't realize! I thought I was helping out by only providing three overloads instead of four. 🙃 I'll look at adding @inlinable, too. Thanks!

This fix-it suppresses the string interpolation warning, but doesn't
provide any additional affordance for dealing with the problem, so it
shouldn't be the first suggestion.
Modify fix-its so the new interpolation API is only recommended
for non-strings (since `??` is a more fluent resolution for strings)
and when used in a `DefaultStringInterpolation` type (since other
interpolation types won't have this API).
@xwu
Copy link
Collaborator

xwu commented Apr 15, 2025

@swift-ci Please smoke test

@natecook1000
Copy link
Member Author

@swift-ci Please smoke test

@natecook1000
Copy link
Member Author

@swift-ci Please smoke test

@natecook1000 natecook1000 requested a review from beccadax April 18, 2025 17:11
@natecook1000
Copy link
Member Author

@swift-ci Please smoke test

@xwu
Copy link
Collaborator

xwu commented Apr 19, 2025

@swift-ci Please build toolchain

@xwu
Copy link
Collaborator

xwu commented Apr 21, 2025

@swift-ci Please build macOS toolchain

@natecook1000
Copy link
Member Author

@swift-ci Please build toolchain macOS platform

@natecook1000
Copy link
Member Author

@swift-ci Please smoke test

@natecook1000
Copy link
Member Author

@swift-ci Please smoke test

@natecook1000
Copy link
Member Author

@swift-ci Please smoke test Linux platform

1 similar comment
@natecook1000
Copy link
Member Author

@swift-ci Please smoke test Linux platform

@natecook1000 natecook1000 removed the swift evolution proposal needed Flag → feature: A feature that warrants a Swift evolution proposal label May 1, 2025
@natecook1000 natecook1000 added the swift evolution approved Flag → feature: A feature that was approved through the Swift evolution process label May 1, 2025
@natecook1000 natecook1000 marked this pull request as ready for review May 1, 2025 06:02
@natecook1000 natecook1000 requested a review from amartini51 May 1, 2025 13:34
@natecook1000 natecook1000 changed the title [DRAFT] Allow a default for optional interpolations [stdlib] Allow a default for optional interpolations May 1, 2025
Copy link
Member

@amartini51 amartini51 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good — left minor feedback and a suggestion.

/// Interpolates the given optional value's textual representation, or the
/// specified default string, into the string literal being created.
///
/// You don't need to call this method directly. It is used by the compiler
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and in the other places this phrasing appears, missing a contraction:

Suggested change
/// You don't need to call this method directly. It is used by the compiler
/// You don't need to call this method directly. It's used by the compiler

///
/// You don't need to call this method directly. It is used by the compiler
/// when interpreting string interpolations where you provide a `default`
/// parameter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code listing would benefit from a sentence introducing it — that might also give you an earlier place to frame the fact that the default interpolation value is used when the value's nil.

@amartini51
Copy link
Member

Changes in 3973a36 look good to me!

@natecook1000
Copy link
Member Author

Now that this has evolution approval, it's ready for review – cc @beccadax @Azoy

/// - value: The value to include in a string interpolation, if non-`nil`.
/// - default: The string to include if `value` is `nil`.
@backDeployed(before: SwiftStdlib 6.2)
@inlinable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for @backDeployed+@inlinable instead of just @_alwaysEmitIntoClient?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seemed like the right set, presuming that @inlinable has different behavior than @_alwaysEmit.... Do we have guidance about which to prefer/when between inlinable and always-emit?

Copy link
Contributor

@Azoy Azoy May 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are very simple functions and I'm unsure if we want to commit these as ABI. With AEIC, the stdlib doesn't take the ABI hit and clients who don't use it don't pay for it either.

@natecook1000
Copy link
Member Author

@swift-ci Please smoke test

@natecook1000
Copy link
Member Author

@swift-ci Please smoke test Windows platform

@natecook1000 natecook1000 merged commit e68069f into swiftlang:main May 7, 2025
3 checks passed
@natecook1000 natecook1000 deleted the string-interp-default branch May 7, 2025 17:47
natecook1000 added a commit that referenced this pull request May 7, 2025
This adds an `appendInterpolation` overload to
`DefaultStringInterpolation` that includes a parameter for providing a
default string when the value to interpolate is `nil`. This allows this
kind of usage:

```swift
let age: Int? = nil
print("Your age is \(age, default: "timeless")")
// Prints "Your age is timeless"
```

The change includes an additional fixit when optional values are
interpolated, with a suggestion to use this `default:` parameter.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
standard library Area: Standard library umbrella swift evolution approved Flag → feature: A feature that was approved through the Swift evolution process
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants