Promote "primitive" types to enums in extensions

Thanks for the explanation, which makes me think that it should be suitable, as you comment, to promote API notes to a “first class” transition technology.

As a first approach towards that goal, would it be ok to expand the notion of “bridging headers” to also include API notes? In that case, it could be appropriate to develop a new C syntax to refer to YAML API notes inside bridging headers. For instance, something like this could be nice:

apinotes “notes.yaml”

···

El 29 mar 2016, a las 18:45, Douglas Gregor <dgregor@apple.com> escribió:

On Mar 29, 2016, at 3:02 AM, Carlos Rodríguez Domínguez <carlos@everywaretech.es> wrote:

Well, those proposal are more oriented towards annotating on C/Objective-C files to allow a more sophisticate import into swift.

Yes, that is true. The philosophy behind these is that it’s better to automatically transform (via annotation) than manually wrap. Naturally, such transformations cannot handle everything.

However, my proposal is to be able to directly annotate in swift, in order to fix “old-style” imports, autogenerated code, etc. Please, allow me to repeat myself, but consider the example of Core Data, in which model classes are autogenerated from a graphical model that, for instance, lacks from enums’ support. Therefore, if we use Core Data, then we can not use enums (Please, take a look at the proposal named "[swift-evolution] Promote "primitive" types to enums in extensions” in order to understand the intention of my proposal as a whole).

There is a Clang attribute “swift_private” that prefixes the name of the declaration with “__” when it is imported into Swift. That way, you can wrap it with a different, more Swift-friendly, API that calls the “__” version.

Note that we do have a mechanism for annotating C/Objective-C APIs without modifying the headers, called “API notes”. It’s a simple YAML format that lets us describe various Clang attributes for entities, e.g., provide the Swift name for a given C function, mark a type as unavailable in Swift, and so on. It’s semi-documented in the swift-clang sources:

  https://github.com/apple/swift-clang/blob/upstream-with-swift/lib/APINotes/APINotesYAMLCompiler.cpp

Essentially, one writes a YAML file for each module that needs annotation. API notes was designed as a transitional technology, so it’s a bit under-designed for a general-purpose tool. However, as we add more Clang-side annotations to improve the mapping of C/Objective-C APIs into Swift, it’s becoming more likely that API notes could/should grow into a more general mechanism for adapting existing C/Objective-C APIs to Swift without manually wrapping everything.

  - Doug

El 28 mar 2016, a las 7:29, Douglas Gregor <dgregor@apple.com> escribió:

On Mar 25, 2016, at 3:25 AM, Carlos Rodríguez Domínguez via swift-evolution <swift-evolution@swift.org> wrote:

(Please, take a look at the proposal named "[swift-evolution] Promote "primitive" types to enums in extensions” in order to understand the intention of my proposal as a whole)

This proposal intends to allow developers to rewrite func signatures to better adapt certain imported C functions to swift. For instance, the function to create a POSIX socket has a C signature like this:

int socket(int domain, int type, int protocol);

In swift, it is currently imported like this:

func socket(_: Int32, _: Int32, _: Int32) -> Int32

However, by documentation, the first parameter should be one of a set of constants beginning with PF_. The second one should be either SOCK_STREAM, SOCK_DGRAM or SOCK_RAW. The third one should be a constant specifying the protocol to use. Finally, the result could be either -1 (to indicate an error) or another integer to indicate that a socket descriptor has been returned.

As you can observe, that old-style signature is highly error prone, does not comply with swift guidelines, it is difficult to understand, etc. My opinion is that there should be some syntax to rewrite the signature to avoid those issues. For instance, a possible syntax could be:

#mapsignature(socket(_:,_:,_:)->Int32)
func socket(domain:SocketDomain, type:SocketType, protocol:SocketProtocol) -> socket_t? {
  let result = socket(domain.rawValue, type.rawValue, protocol.rawValue)
  if result == -1 {
    return nil
  }
  else{
    return result
  }
}

Note that the compiler should enforce a function call to the original function that we are rewriting.

After a rewriting has happened, three options may be considered: either to allow the original function to be called, to avoid the original function to be called (through a compiler error with a fix-it) or to emit a warning, advising the developer to adopt the rewritten signature.

Anyhow, this proposal should allow a greatly increased interoperability between old style code and swift, which, in my opinion, is quite “forced” right now.

FWIW, there have been a number of proposals in roughly this space, using annotations in the C headers to improve the mapping into Swift, including

  Import as member: https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md
  Import Objective-C constants as Swift types: https://github.com/apple/swift-evolution/blob/master/proposals/0033-import-objc-constants.md
  Better translation of Objective-C APIs into Swift (the swift_name attribute part, at least): https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md

  - Doug

That’s a really good syntax. Maybe the new @preferred could complete the current @available, by also providing fix-it support. Note that it should be enforced to have “compatible” parameters and return types between the old-style func and the preferred one, as opposed to the current @available, which does not have this enforcement (thus not providing fix-it support).

···

El 26 mar 2016, a las 22:00, Brent Royal-Gordon <brent@architechies.com> escribió:

What's wrong with just overloading it like this without hiding the original? That way you can start by directly porting (perhaps even mechanically converting) the code and later refine it to use the nicer Swift-style overload.

Well, the difference is that the compiler could produce a warning or error with fixit to indicate the developer that an alternative signature to the originally C imported one is available. This way, code ports should gain much readability, while not requiring much effort from the developer to make use of the newer signature.

If that's all you want, maybe we can have an attribute which says "prefer this version over that one":

  @preferred(since: 3.0, over: socket(_: Int32, _: Int32, _: Int32) -> Int32)
  func socket(domain:SocketDomain, type:SocketType, protocol:SocketProtocol) -> socket_t? {
    let result = socket(domain.rawValue, type.rawValue, protocol.rawValue)
    if result == -1 {
      return nil
    }
    else{
      return result
    }
  }

This would effectively apply an `@available(deprecated: 3.0, renamed: socket(domain: SocketDomain, type: SocketType, protocol: SocketProtocol) -> socket_t?)` to the other API.

(Or, for that matter, you could simply use an API note to apply that deprecation to the other API, which would not require any new features.)

--
Brent Royal-Gordon
Architechies

Thanks for the explanation, which makes me think that it should be suitable, as you comment, to promote API notes to a “first class” transition technology.

As a first approach towards that goal, would it be ok to expand the notion of “bridging headers” to also include API notes? In that case, it could be appropriate to develop a new C syntax to refer to YAML API notes inside bridging headers. For instance, something like this could be nice:

apinotes “notes.yaml”

I think of API notes like module maps: it’s an external format that lays additional semantic information on top of existing headers without requiring you to modify those headers (because you often don’t control them).

  - Doug

···

On Apr 24, 2016, at 3:45 PM, Carlos Rodríguez Domínguez <carlos@everywaretech.es> wrote:

El 29 mar 2016, a las 18:45, Douglas Gregor <dgregor@apple.com> escribió:

On Mar 29, 2016, at 3:02 AM, Carlos Rodríguez Domínguez <carlos@everywaretech.es> wrote:

Well, those proposal are more oriented towards annotating on C/Objective-C files to allow a more sophisticate import into swift.

Yes, that is true. The philosophy behind these is that it’s better to automatically transform (via annotation) than manually wrap. Naturally, such transformations cannot handle everything.

However, my proposal is to be able to directly annotate in swift, in order to fix “old-style” imports, autogenerated code, etc. Please, allow me to repeat myself, but consider the example of Core Data, in which model classes are autogenerated from a graphical model that, for instance, lacks from enums’ support. Therefore, if we use Core Data, then we can not use enums (Please, take a look at the proposal named "[swift-evolution] Promote "primitive" types to enums in extensions” in order to understand the intention of my proposal as a whole).

There is a Clang attribute “swift_private” that prefixes the name of the declaration with “__” when it is imported into Swift. That way, you can wrap it with a different, more Swift-friendly, API that calls the “__” version.

Note that we do have a mechanism for annotating C/Objective-C APIs without modifying the headers, called “API notes”. It’s a simple YAML format that lets us describe various Clang attributes for entities, e.g., provide the Swift name for a given C function, mark a type as unavailable in Swift, and so on. It’s semi-documented in the swift-clang sources:

  https://github.com/apple/swift-clang/blob/upstream-with-swift/lib/APINotes/APINotesYAMLCompiler.cpp

Essentially, one writes a YAML file for each module that needs annotation. API notes was designed as a transitional technology, so it’s a bit under-designed for a general-purpose tool. However, as we add more Clang-side annotations to improve the mapping of C/Objective-C APIs into Swift, it’s becoming more likely that API notes could/should grow into a more general mechanism for adapting existing C/Objective-C APIs to Swift without manually wrapping everything.

  - Doug

El 28 mar 2016, a las 7:29, Douglas Gregor <dgregor@apple.com> escribió:

On Mar 25, 2016, at 3:25 AM, Carlos Rodríguez Domínguez via swift-evolution <swift-evolution@swift.org> wrote:

(Please, take a look at the proposal named "[swift-evolution] Promote "primitive" types to enums in extensions” in order to understand the intention of my proposal as a whole)

This proposal intends to allow developers to rewrite func signatures to better adapt certain imported C functions to swift. For instance, the function to create a POSIX socket has a C signature like this:

int socket(int domain, int type, int protocol);

In swift, it is currently imported like this:

func socket(_: Int32, _: Int32, _: Int32) -> Int32

However, by documentation, the first parameter should be one of a set of constants beginning with PF_. The second one should be either SOCK_STREAM, SOCK_DGRAM or SOCK_RAW. The third one should be a constant specifying the protocol to use. Finally, the result could be either -1 (to indicate an error) or another integer to indicate that a socket descriptor has been returned.

As you can observe, that old-style signature is highly error prone, does not comply with swift guidelines, it is difficult to understand, etc. My opinion is that there should be some syntax to rewrite the signature to avoid those issues. For instance, a possible syntax could be:

#mapsignature(socket(_:,_:,_:)->Int32)
func socket(domain:SocketDomain, type:SocketType, protocol:SocketProtocol) -> socket_t? {
  let result = socket(domain.rawValue, type.rawValue, protocol.rawValue)
  if result == -1 {
    return nil
  }
  else{
    return result
  }
}

Note that the compiler should enforce a function call to the original function that we are rewriting.

After a rewriting has happened, three options may be considered: either to allow the original function to be called, to avoid the original function to be called (through a compiler error with a fix-it) or to emit a warning, advising the developer to adopt the rewritten signature.

Anyhow, this proposal should allow a greatly increased interoperability between old style code and swift, which, in my opinion, is quite “forced” right now.

FWIW, there have been a number of proposals in roughly this space, using annotations in the C headers to improve the mapping into Swift, including

  Import as member: https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md
  Import Objective-C constants as Swift types: https://github.com/apple/swift-evolution/blob/master/proposals/0033-import-objc-constants.md
  Better translation of Objective-C APIs into Swift (the swift_name attribute part, at least): https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md

  - Doug

This capability sounds useful in other situations where some outside API is undesirable in your code. For example in the Swift standard library itself we have our own "halt with an error" functions such as fatalError(). We almost always want to use those instead of something like abort() or exit(). It would be useful to mark fatalError() as the preferred replacement for abort() and exit(). Then the compiler would help enforce our policy.

(In C code we could enforce such non-usage with ugly things like `#define abort() dont_call_abort()`. That ugly solution is not available in Swift.)

···

On Mar 26, 2016, at 2:00 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

If that's all you want, maybe we can have an attribute which says "prefer this version over that one":

  @preferred(since: 3.0, over: socket(_: Int32, _: Int32, _: Int32) -> Int32)
  func socket(domain:SocketDomain, type:SocketType, protocol:SocketProtocol) -> socket_t? {
    …
  }

This would effectively apply an `@available(deprecated: 3.0, renamed: socket(domain: SocketDomain, type: SocketType, protocol: SocketProtocol) -> socket_t?)` to the other API.

--
Greg Parker gparker@apple.com Runtime Wrangler

+1 for me too, sounds like a very useful syntax with a not incredibly huge cost.

[[iOS messageWithContent:webContent] broadcast]

···

On 27 Mar 2016, at 09:31, Carlos Rodríguez Domínguez via swift-evolution <swift-evolution@swift.org> wrote:

That’s a really good syntax. Maybe the new @preferred could complete the current @available, by also providing fix-it support. Note that it should be enforced to have “compatible” parameters and return types between the old-style func and the preferred one, as opposed to the current @available, which does not have this enforcement (thus not providing fix-it support).

El 26 mar 2016, a las 22:00, Brent Royal-Gordon <brent@architechies.com> escribió:

What's wrong with just overloading it like this without hiding the original? That way you can start by directly porting (perhaps even mechanically converting) the code and later refine it to use the nicer Swift-style overload.

Well, the difference is that the compiler could produce a warning or error with fixit to indicate the developer that an alternative signature to the originally C imported one is available. This way, code ports should gain much readability, while not requiring much effort from the developer to make use of the newer signature.

If that's all you want, maybe we can have an attribute which says "prefer this version over that one":

   @preferred(since: 3.0, over: socket(_: Int32, _: Int32, _: Int32) -> Int32)
   func socket(domain:SocketDomain, type:SocketType, protocol:SocketProtocol) -> socket_t? {
       let result = socket(domain.rawValue, type.rawValue, protocol.rawValue)
       if result == -1 {
           return nil
       }
       else{
           return result
       }
   }

This would effectively apply an `@available(deprecated: 3.0, renamed: socket(domain: SocketDomain, type: SocketType, protocol: SocketProtocol) -> socket_t?)` to the other API.

(Or, for that matter, you could simply use an API note to apply that deprecation to the other API, which would not require any new features.)

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution