Files
swift-mirror/test/attr/attr_c_with_objc.swift
Xi Ge b1ee9e7456 c-interop: teach @c to handle ObjC type metatype Class. rdar://179401427
ClangImporter imports C 'Class' as AnyClass, but declaring a C-interop
function that takes/returns it via @c or @_extern(c) was rejected. Support it
across three layers:

Sema: make AnyClass (an ExistentialMetatypeType of AnyObject) representable in
ForeignLanguage::C, gated on ObjC interop since 'Class' is an ObjC runtime
type. Reuse getObjCObjectRepresentable so C and ObjC agree on the kind.

SILGen: a @c/@_extern(c) function is emitted directly as its C entry point
(no separate native thunk), so bridge the metatype at that boundary in place:
objc_to_thick_metatype on a 'Class' parameter in the prolog, and
thick_to_objc_metatype on an AnyClass result in the epilog. Both are no-ops
unless the native and foreign SIL types differ.

PrintAsClang: keep the @c declaration in the plain-C section and emit
'#include <objc/objc.h>' there when a @c decl uses 'Class', so the generated
header stays usable from plain C.
2026-07-12 08:25:22 -07:00

53 lines
2.6 KiB
Swift

// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s
// REQUIRES: objc_interop
import Foundation
@objc
class ObjCClass: NSObject { }
@c(objcClassReturn) func objcClassReturn() -> ObjCClass { fatalError() }
// expected-error @-1 {{global function cannot be marked '@c' because its result type cannot be represented in C}}
// expected-note @-2 {{classes cannot be represented in C}}
@c(objcClassParams) func objcClassParams(a: ObjCClass, b: ObjCClass) { }
// expected-error @-1 {{global function cannot be marked '@c' because the type of the parameter 1 cannot be represented in C}}
// expected-error @-2 {{global function cannot be marked '@c' because the type of the parameter 2 cannot be represented in C}}
// expected-note @-3 2 {{classes cannot be represented in C}}
@objc
protocol ObjCProtocol {}
@c(objcProtocol) func objcProtocol(a: ObjCProtocol) { }
// expected-error @-1 {{global function cannot be marked '@c' because the type of the parameter cannot be represented in C}}
// expected-note @-2 {{protocols cannot be represented in C}}
@objc
enum ObjCEnum: Int { case A, B }
@c(objcEnumUseInCDecl) func objcEnumUseInCDecl(a: ObjCEnum) { }
// expected-error @-1 {{global function cannot be marked '@c' because the type of the parameter cannot be represented in C}}
// expected-note @-2 {{Swift enums not marked '@c' cannot be represented in C}}
/// Objective-C accepts @c enums.
@c(CEnum)
enum CEnum: Int { case A, B }
@_cdecl("cdeclEnumUseInObjc") func cdeclEnumUseInObjc(a: CEnum) { }
enum SwiftEnum { case A, B }
@_cdecl("swiftEnumUseInObjc") func swiftEnumUseInObjc(a: SwiftEnum) { }
// expected-error @-1 {{global function cannot be marked '@_cdecl' because the type of the parameter cannot be represented in Objective-C}}
// expected-note @-2 {{Swift enums not marked '@c' or '@objc' cannot be represented in Objective-C}}
/// AnyClass maps to the C runtime type Class.
@c(anyClassParam) func anyClassParam(a: AnyClass) { }
@c(anyClassReturn) func anyClassReturn() -> AnyClass { fatalError() }
/// The object form (AnyObject) and protocol-qualified class metatypes (P.Type,
/// i.e. C's Class<P>) are still rejected: only bare AnyClass is representable.
@c(anyObjectParam) func anyObjectParam(a: AnyObject) { }
// expected-error @-1 {{global function cannot be marked '@c' because the type of the parameter cannot be represented in C}}
// expected-note @-2 {{protocols cannot be represented in C}}
@c(objcProtoMetatype) func objcProtoMetatype(a: ObjCProtocol.Type) { }
// expected-error @-1 {{global function cannot be marked '@c' because the type of the parameter cannot be represented in C}}