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-6679] Can't naivly create a global cString #49228

Open
gottesmm opened this issue Dec 30, 2017 · 9 comments
Open

[SR-6679] Can't naivly create a global cString #49228

gottesmm opened this issue Dec 30, 2017 · 9 comments
Labels
compiler The Swift compiler in itself improvement

Comments

@gottesmm
Copy link
Member

Previous ID SR-6679
Radar rdar://problem/36240545
Original Reporter @gottesmm
Type Improvement
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Improvement
Assignee None
Priority Medium

md5: 62988c50729d9a8f4bc240d3701dcde6

Issue Description:

With a near ToT compiler, I can't naively create a global cString.

let fn = String(cString: "123456")

Output:

tmp.swift:2:10: error: ambiguous use of 'init(cString:)'
let fn = String(cString: "123456")
         ^
Swift.String:29:12: note: found this candidate
    public init(cString: UnsafePointer<CChar>)
           ^
Swift.String:35:12: note: found this candidate
    public init(cString: UnsafePointer<UInt8>)
           ^

I am not sure if this goes to the typechecker or the stdlib. Shouldn't cString just take an UnsafeRawPointer?

@gottesmm
Copy link
Member Author

@swift-ci sync

@gottesmm
Copy link
Member Author

@swift-ci create

@atrick
Copy link
Member

atrick commented Dec 30, 2017

It's not clear from the problem description what we're trying to accomplish (why pass a string literal to the cString initializer?).

But there's a known deficiency in StaticString that's probably the fundamental problem. There's currently no way to get a CString from a StaticString without calling strdup.

We should probably add a .cString property to StaticString to allow this.
The other possiblity would be to make UnsafePointer<Int8> ExpressibleByStringLiteral.

@gottesmm
Copy link
Member Author

Let me explain what I am trying to do. I was reading this post from Helge Hesse:

http://www.alwaysrightinstitute.com/swift-cstr/

All I was trying to do was create an example where we have /no/ mallocs so I could respond to his post. First I thought part of the problem is that String's static cstring case wasn't recognizing that we had a static string or something like that. So first I tried:

  let f = "abcdef.yyy"

and discovered that if I used:

  f.withCString(...)

we performed a copy it looks like. So I tried to use the cString initializer hoping that I would hit a fast path in the code and was surprised that I couldn't pass a global string to that constructor.

@atrick
Copy link
Member

atrick commented Dec 31, 2017

I can see two issues with CString APIs: string literals, and null termination.

1. string literals

StaticString is the way to avoid heap allocation for literals. Unfortunately there's no way to get a CString out. We definitely want some solution to this. Probably one of:

  • StaticString.cString property

  • StaticCString type

  • UnsafePointer<CChar> directly ExpressibleByStringLiteral

The answer we choose probably depends on the answer to the null termination problem. The nice thing is that, since StaticString is immutable, we don't need any closure-based withCString API.

2. null termination

It seems that, in addition to avoiding heap allocation for literals, the user wants to generally expose String guts to CString APIs without a copy. I don't know if this has been discussed before, but I see two possibilities.

  • the user should only deal in UnsafePointer<CChar> (or StaticString once we fix that). If you want to use native Swift features, you'll get a copy each time you go back and forth and that's just expected.
  • a String variant optimized for CString APIs that allows a null-terminated UTF-8 representation. withCString would still need to rebind memory to CChar, but that's "free".

@atrick
Copy link
Member

atrick commented Dec 31, 2017

+cc @belkadan, @milseman

@belkadan
Copy link
Contributor

belkadan commented Jan 2, 2018

init(cString:) is for starting with a C string and getting a String. That's a different request than "make a statically-allocated C string", but is actually still reasonable, since normally we allow passing a String as a pointer (including string literals). I'm a little curious why that doesn't compile.

As far as actually getting a compile-time-guaranteed statically-allocated C string, I do think improving StaticString is the way to go, but I forget if StaticString guarantees a UTF-8 representation. I think it does, though, and that's probably what we want for C strings in Swift. (At some point we might also want to improve static representation of non-UTF-8 data, for data that is mostly text but might have specific non-textual bytes interspersed. C allows this in plain string literals with \x, but Swift always names Unicode codepoints, which makes it impossible to specify single bytes with values greater than 0x7F.)

Other relevant things: we should be able to statically allocate buffers usable by String, and we should be able to optimize out string-literal-to-String-to-C-string conversions.

@gottesmm
Copy link
Member Author

gottesmm commented Jan 4, 2018

Exactly. And we should document this better and make it clear that this is the blessed way to accomplish this simple task. I completely forgot about StaticString.

@atrick
Copy link
Member

atrick commented Jan 4, 2018

@belkadan Swift UTF-8 strings are UInt8, CStrings are CChar. `cString` is overloaded to handle either one. String also implicitly casts to either. Trying to do two conversions in one expression is an ambiguity. It would be nice if the type checker just gave precedence to the UInt8 conversion.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler The Swift compiler in itself improvement
Projects
None yet
Development

No branches or pull requests

3 participants