String literal not inferred to StaticString

I'm wondering why this String literal isn't inferred to StaticString:

extension StaticString {
    var foo: String { "foo" }
}
"str".foo //error: Value of type 'String' has no member 'foo'

It works as I'd expect for Int literals:

extension Double {
    var foo: String { "foo" }
}
1.foo // "foo"

Integer and floating point literals are separate concepts to the parser. 1 is valid for either. Thus 1.foo tries an integer literal as Int, finds nothing, and then tries a floating point literal as Double and it works. But it is still just using the default for that type of literal. If you change the extension to UInt8 or Float32, it won’t work.

2 Likes

I think the answer to why "str" is considered a String, and not a StaticString, is historical and a result of the need, at least for Apple, to have Swift interoperate with Objective-C/C++/C/C++/et al. StaticString is a pure Swift value type. Considering "str" as a StaticString would probably break a lot code out there that relies on Swift/non-Swift language interoperability. If you tried StaticString(stringliteral: "str").foo, everything would probably work out like you wanted.

The type of a base expression has to be resolved before its members can be looked up. "str" by itself has no type context to direct the type checker, so it uses the default type for string literals, which is String. You'd have to write ("str" as StaticString).foo to direct the type checker to resolve the literal as a StaticString.

What do you want to use StaticString for? There's little reason to use it anymore.

2 Likes

Thank you for the explanations. Makes sense.

I'm not using StaticString currently but when reading Pavel's recent blog post about diagnostics and inference, I was reminded of a past project where I saw this behavior. IIRC, we wanted to create a StaticString extension for localization purposes.

But then I wonder why the other example compiles:

extension Double {
    var foo: String { "foo" }
}
1.foo // "foo"

The default type for an integer literal is Int, but the compiler apparently infers the type as Double from the context. What is different here?

1 Like

The compiler treats Double as an alternative literal type for ExpressibleByIntegerLiteral.

If you scroll up a couple cases you will notice ExpressibleByStringLiteral and ExpressibleByExtendedGraphemeClusterLiteral are not given any alternatives, so a String literal will not be resolved to a Character without context either.

extension Character {
    var foo: Bool { true }
}
"a".foo // error: value of type 'String' has no member 'foo'

let char: Character = "a" // OK

I see, that explains it, thanks. So the “integer literal as floating point constant” treatment is a special case, built-in into the compiler.

1 Like

Yeah, another way to think of it is that all integer literals are also floating-point literals.

Terms of Service

Privacy Policy

Cookie Policy