Allow @NSManaged to be applied to methods.

Core Data synthesizes Key-Value-Coding-compliant accessors for @NSManaged
properties, but Swift won't allow them to be called without predeclaring
them.

In practice, '@NSManaged' on a method is the same as 'dynamic', except
you /can't/ provide a body and overriding it won't work. This is not the
long-term model we want (see rdar://problem/20829214), but it fixes a
short-term issue with an unfortunate workaround (go through
mutableOrderedSetValueForKey(_:) and similar methods).

rdar://problem/17583057

Swift SVN r30523
This commit is contained in:
Jordan Rose
2015-07-23 02:08:55 +00:00
parent d980700f17
commit 0733ba42c9
17 changed files with 161 additions and 80 deletions

View File

@@ -122,7 +122,7 @@ SIMPLE_DECL_ATTR(IBInspectable, IBInspectable,
SIMPLE_DECL_ATTR(IBOutlet, IBOutlet,
OnVar, 14)
SIMPLE_DECL_ATTR(NSManaged, NSManaged, OnVar, 15)
SIMPLE_DECL_ATTR(NSManaged, NSManaged, OnVar | OnFunc, 15)
SIMPLE_DECL_ATTR(lazy, Lazy, OnVar|DeclModifier, 16)

View File

@@ -1302,8 +1302,8 @@ ERROR(noescape_conflicts_escaping_autoclosure,attribute_parsing,none,
// NSManaged attribute
ERROR(attr_NSManaged_not_property,sema_tcd,none,
"@NSManaged only allowed on a property in a class", ())
ERROR(attr_NSManaged_not_instance_member,sema_tcd,none,
"@NSManaged only allowed on an instance property or method", ())
ERROR(attr_NSManaged_not_stored,sema_tcd,none,
"@NSManaged not allowed on %select{computed|observing|adressed}0 "
"properties", (unsigned))
@@ -1313,6 +1313,8 @@ ERROR(attr_NSManaged_initial_value,sema_tcd,none,
"@NSManaged property cannot have an initial value", ())
ERROR(attr_NSManaged_NSCopying,sema_tcd,none,
"@NSManaged property cannot also be marked @NSCopying", ())
ERROR(attr_NSManaged_method_body,sema_tcd,none,
"@NSManaged method cannot have a body; it must be provided at runtime",())
// NSCopying attribute

View File

@@ -51,7 +51,7 @@ const uint16_t VERSION_MAJOR = 0;
/// To ensure that two separate changes don't silently get merged into one
/// in source control, you should also update the comment to briefly
/// describe what change you made.
const uint16_t VERSION_MINOR = 206; // Last change: add reabstraction_thunk type
const uint16_t VERSION_MINOR = 207; // Last change: @NSManaged on methods
using DeclID = Fixnum<31>;
using DeclIDField = BCFixed<31>;

View File

@@ -110,12 +110,17 @@ private:
if (VD->isFinal() && VD->getOverriddenDecl() == nullptr)
continue;
// @NSManaged properties and methods don't have vtable entries.
if (member->getAttrs().hasAttribute<NSManagedAttr>())
continue;
// Add entries for methods.
if (auto fn = dyn_cast<FuncDecl>(member)) {
// Ignore accessors. These get added when their AbstractStorageDecl is
// visited.
if (fn->isAccessor())
continue;
addMethodEntries(fn);
} else if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
// Stub constructors don't get an entry.
@@ -128,9 +133,6 @@ private:
// FIXME: Stored properties should either be final or have accessors.
if (!asd->hasAccessorFunctions()) continue;
// @NSManaged properties don't have vtable entries.
if (asd->getAttrs().hasAttribute<NSManagedAttr>()) continue;
addMethodEntries(asd->getGetter());
if (auto *setter = asd->getSetter())
addMethodEntries(setter);

View File

@@ -1153,6 +1153,10 @@ namespace {
// var/subscript.
if (method->isAccessor()) return;
// Don't emit getters/setters for @NSManaged methods.
if (method->getAttrs().hasAttribute<NSManagedAttr>())
return;
llvm::Constant *entry = emitObjCMethodDescriptor(IGM, method);
// This pointer will be set if we need to store the extended method type
// encoding.
@@ -1480,7 +1484,7 @@ namespace {
if (llvm::Constant *prop = buildProperty(var))
Properties.push_back(prop);
// Don't emit getter/setter descriptors for @NSManagedAttr properties.
// Don't emit getter/setter descriptors for @NSManaged properties.
if (var->getAttrs().hasAttribute<NSManagedAttr>() ||
// Don't emit descriptors for properties without accessors.
var->getGetter() == nullptr)

View File

@@ -146,6 +146,11 @@ public:
void visitFuncDecl(FuncDecl *method) {
if (!requiresObjCMethodDescriptor(method)) return;
// Don't emit getters/setters for @NSManaged methods.
if (method->getAttrs().hasAttribute<NSManagedAttr>())
return;
llvm::Constant *name, *imp, *types;
emitObjCMethodDescriptorParts(IGM, method,
/*extended*/false,
@@ -203,7 +208,7 @@ public:
// ObjC doesn't have a notion of class properties, so we'd only do this
// for instance properties.
// Don't emit getters/setters for @NSManagedAttr properties.
// Don't emit getters/setters for @NSManaged properties.
if (prop->getAttrs().hasAttribute<NSManagedAttr>())
return;

View File

@@ -173,12 +173,15 @@ SILValue SILGenFunction::emitDynamicMethodRef(SILLocation loc,
bool SILGenModule::requiresObjCMethodEntryPoint(FuncDecl *method) {
// Property accessors should be generated alongside the property unless
// the @NSManagedAttr attribute is present.
// the @NSManaged attribute is present.
if (method->isGetterOrSetter()) {
auto asd = method->getAccessorStorageDecl();
return asd->isObjC() && !asd->getAttrs().hasAttribute<NSManagedAttr>();
}
if (method->getAttrs().hasAttribute<NSManagedAttr>())
return false;
return method->isObjC() || method->getAttrs().hasAttribute<IBActionAttr>();
}

View File

@@ -333,12 +333,23 @@ void AttributeEarlyChecker::visitIBOutletAttr(IBOutletAttr *attr) {
}
void AttributeEarlyChecker::visitNSManagedAttr(NSManagedAttr *attr) {
// @NSManaged may only be used on properties.
auto *VD = cast<VarDecl>(D);
// @NSManaged only applies to instance methods and properties within a class.
if (cast<ValueDecl>(D)->isStatic() ||
!D->getDeclContext()->isClassOrClassExtensionContext()) {
return diagnoseAndRemoveAttr(attr,
diag::attr_NSManaged_not_instance_member);
}
// NSManaged only applies to non-class properties within a class.
if (VD->isStatic() || !VD->getDeclContext()->isClassOrClassExtensionContext())
return diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_not_property);
if (auto *method = dyn_cast<FuncDecl>(D)) {
// Separate out the checks for methods.
if (method->hasBody())
return diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_method_body);
return;
}
// Everything below deals with restrictions on @NSManaged properties.
auto *VD = cast<VarDecl>(D);
if (VD->isLet())
return diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_let_property);

View File

@@ -3903,10 +3903,11 @@ public:
if (decl->isInvalid() || decl->isImplicit() || decl->hasClangNode())
return false;
// Functions can have asmname and semantics attributes.
// Functions can have asmname, semantics, and NSManaged attributes.
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
if (func->getAttrs().hasAttribute<AsmnameAttr>() ||
func->getAttrs().hasAttribute<SemanticsAttr>())
func->getAttrs().hasAttribute<SemanticsAttr>() ||
func->getAttrs().hasAttribute<NSManagedAttr>())
return false;
}
@@ -4158,28 +4159,7 @@ public:
validateAttributes(TC, FD);
// Member functions need some special validation logic.
if (auto contextType = FD->getDeclContext()->getDeclaredTypeInContext()) {
// If this is a class member, mark it final if the class is final.
if (auto cls = contextType->getClassOrBoundGenericClass()) {
if (cls->isFinal() && !FD->isAccessor() &&
!FD->isFinal() && !FD->isDynamic()) {
makeFinal(TC.Context, FD);
}
// static func declarations in classes are synonyms
// for `class final func` declarations.
if (FD->getStaticSpelling() == StaticSpellingKind::KeywordStatic) {
auto finalAttr = FD->getAttrs().getAttribute<FinalAttr>();
if (finalAttr) {
auto finalRange = finalAttr->getRange();
if (finalRange.isValid())
TC.diagnose(finalRange.Start, diag::decl_already_final)
.highlight(finalRange)
.fixItRemove(finalRange);
}
makeFinal(TC.Context, FD);
}
}
if (FD->getDeclContext()->isTypeContext()) {
if (!checkOverrides(TC, FD)) {
// If a method has an 'override' keyword but does not
// override anything, complain.
@@ -4239,6 +4219,29 @@ public:
TC.checkDeclAttributes(FD);
// If this is a class member, mark it final if the class is final.
if (auto contextType = FD->getDeclContext()->getDeclaredTypeInContext()) {
if (auto cls = contextType->getClassOrBoundGenericClass()) {
if (cls->isFinal() && !FD->isAccessor() &&
!FD->isFinal() && !FD->isDynamic()) {
makeFinal(TC.Context, FD);
}
// static func declarations in classes are synonyms
// for `class final func` declarations.
if (FD->getStaticSpelling() == StaticSpellingKind::KeywordStatic) {
auto finalAttr = FD->getAttrs().getAttribute<FinalAttr>();
if (finalAttr) {
auto finalRange = finalAttr->getRange();
if (finalRange.isValid())
TC.diagnose(finalRange.Start, diag::decl_already_final)
.highlight(finalRange)
.fixItRemove(finalRange);
}
makeFinal(TC.Context, FD);
}
}
}
// Check whether we have parameters with default arguments that follow a
// closure parameter; warn about such things, because the closure will not
// be treated as a trailing closure.

View File

@@ -11,4 +11,6 @@ func getMyManagedObject() -> MyManagedObject {
extension MyManagedObject {
@NSManaged var anotherManaged: String
@NSManaged func managedMethod()
}

View File

@@ -53,3 +53,7 @@ func accessOther(om: OtherManagedObject) -> String {
func accessMine(obj: MyManagedObject) -> String {
return obj.anotherManaged
}
func accessMyMethod(obj: MyManagedObject) {
obj.managedMethod()
}

View File

@@ -45,11 +45,12 @@ func method(@#^KEYWORD1^#) {}
@#^KEYWORD2^#
func method(){}
// KEYWORD2: Begin completions, 8 items
// KEYWORD2: Begin completions, 9 items
// KEYWORD2-NEXT: Keyword/None: available[#Func Attribute#]; name=available{{$}}
// KEYWORD2-NEXT: Keyword/None: objc[#Func Attribute#]; name=objc{{$}}
// KEYWORD2-NEXT: Keyword/None: noreturn[#Func Attribute#]; name=noreturn{{$}}
// KEYWORD2-NEXT: Keyword/None: IBAction[#Func Attribute#]; name=IBAction{{$}}
// KEYWORD2-NEXT: Keyword/None: NSManaged[#Func Attribute#]; name=NSManaged{{$}}
// KEYWORD2-NEXT: Keyword/None: inline[#Func Attribute#]; name=inline{{$}}
// KEYWORD2-NEXT: Keyword/None: nonobjc[#Func Attribute#]; name=nonobjc{{$}}
// KEYWORD2-NEXT: Keyword/None: warn_unused_result[#Func Attribute#]; name=warn_unused_result{{$}}

View File

@@ -31,6 +31,7 @@ sil_vtable X {}
@objc class SwiftGizmo : Gizmo {
@objc @NSManaged var x: X
@objc @NSManaged func kvc()
}
sil @_TToFC19objc_attr_NSManaged1XcfMS0_FT_S0_ : $@convention(objc_method) (@owned X) -> @owned X {
bb0(%0 : $X):

View File

@@ -14,8 +14,11 @@ import gizmo
class SwiftGizmo : Gizmo {
@NSManaged var x: X
// CHECK-NOT: sil hidden @_TToFC19objc_attr_NSManaged10SwiftGizmog1xCS_1X : $@convention(objc_method) (SwiftGizmo) -> @autoreleased X
// CHECK-NOT: sil hidden @_TToFC19objc_attr_NSManaged10SwiftGizmos1xCS_1X
@NSManaged func kvc()
// CHECK-NOT: sil hidden @_TToFC19objc_attr_NSManaged10SwiftGizmog1x
// CHECK-NOT: sil hidden @_TToFC19objc_attr_NSManaged10SwiftGizmos1x
// CHECK-NOT: sil hidden @_TToFC19objc_attr_NSManaged10SwiftGizmo3kvc
// Make sure that we're calling through the @objc entry points.
// CHECK-LABEL: sil hidden @_TFC19objc_attr_NSManaged10SwiftGizmo7modifyX{{.*}} : $@convention(method) (@guaranteed SwiftGizmo) -> () {
@@ -28,16 +31,50 @@ class SwiftGizmo : Gizmo {
x = x.foo()
// CHECK: return
}
// CHECK-LABEL: sil hidden @_TFC19objc_attr_NSManaged10SwiftGizmo8testFuncfS0_FT_T_
func testFunc() {
// CHECK: = class_method [volatile] %0 : $SwiftGizmo, #SwiftGizmo.kvc!1.foreign : SwiftGizmo -> () -> () , $@convention(objc_method) (SwiftGizmo) -> ()
// CHECK: return
kvc()
}
}
extension SwiftGizmo {
@NSManaged func extKVC()
// CHECK-LABEL: _TFC19objc_attr_NSManaged10SwiftGizmo7testExtfS0_FT_T_
func testExt() {
// CHECK: = class_method [volatile] %0 : $SwiftGizmo, #SwiftGizmo.extKVC!1.foreign : SwiftGizmo -> () -> () , $@convention(objc_method) (SwiftGizmo) -> ()
// CHECK: return
extKVC()
}
}
final class FinalGizmo : SwiftGizmo {
@NSManaged var y: String
@NSManaged func kvc2()
}
extension FinalGizmo {
@NSManaged func extKVC2()
// CHECK-LABEL: _TFC19objc_attr_NSManaged10FinalGizmo8testExt2fS0_FT_T_
func testExt2() {
// CHECK: = class_method [volatile] %0 : $FinalGizmo, #FinalGizmo.extKVC2!1.foreign : FinalGizmo -> () -> () , $@convention(objc_method) (FinalGizmo) -> ()
// CHECK: return
extKVC2()
}
}
// CHECK-LABEL: sil hidden @_TF19objc_attr_NSManaged9testFinalFCS_10FinalGizmoSS : $@convention(thin) (@owned FinalGizmo) -> @owned String {
func testFinal(obj: FinalGizmo) -> String {
// CHECK: class_method [volatile] %0 : $FinalGizmo, #FinalGizmo.kvc2!1.foreign : FinalGizmo -> () -> () , $@convention(objc_method) (FinalGizmo) -> ()
// CHECK-NOT: return
// CHECK: class_method [volatile] %0 : $FinalGizmo, #FinalGizmo.y!getter.1.foreign : FinalGizmo -> () -> String , $@convention(objc_method) (FinalGizmo) -> @autoreleased NSString
// CHECK: return
obj.kvc2()
return obj.y
}
@@ -50,13 +87,15 @@ func testFinal(obj: FinalGizmo) -> String {
// The vtable should not contain any entry points for getters and setters.
// CHECK-LABEL: sil_vtable SwiftGizmo {
// CHECK-NEXT: #SwiftGizmo.modifyX!1: _TFC19objc_attr_NSManaged10SwiftGizmo7modifyXfS0_FT_T_ // objc_attr_NSManaged.SwiftGizmo.modifyX (objc_attr_NSManaged.SwiftGizmo)() -> ()
// CHECK-NEXT: #SwiftGizmo.deinit!deallocator:
// CHECK-NEXT: #SwiftGizmo.testFunc!1: _TFC19objc_attr_NSManaged10SwiftGizmo8testFuncfS0_FT_T_ // objc_attr_NSManaged.SwiftGizmo.testFunc (objc_attr_NSManaged.SwiftGizmo)() -> ()
// CHECK-NEXT: #SwiftGizmo.init!initializer.1: _TFC19objc_attr_NSManaged10SwiftGizmocfMS0_FT_GSQS0__
// CHECK-NEXT: #SwiftGizmo.init!initializer.1: _TFC19objc_attr_NSManaged10SwiftGizmocfMS0_FT7bellsOnSi_GSQS0__
// CHECK-NEXT: #SwiftGizmo.deinit!deallocator:
// CHECK-NEXT: }
// CHECK-LABEL: sil_vtable FinalGizmo {
// CHECK-NEXT: #SwiftGizmo.modifyX!1: _TFC19objc_attr_NSManaged10SwiftGizmo7modifyXfS0_FT_T_
// CHECK-NEXT: #SwiftGizmo.testFunc!1: _TFC19objc_attr_NSManaged10SwiftGizmo8testFuncfS0_FT_T_ // objc_attr_NSManaged.SwiftGizmo.testFunc (objc_attr_NSManaged.SwiftGizmo)() -> ()
// CHECK-NEXT: #SwiftGizmo.init!initializer.1: _TFC19objc_attr_NSManaged10FinalGizmocfMS0_FT_GSQS0__
// CHECK-NEXT: #SwiftGizmo.init!initializer.1: _TFC19objc_attr_NSManaged10FinalGizmocfMS0_FT7bellsOnSi_GSQS0__
// CHECK-NEXT: #FinalGizmo.deinit!deallocator: _TFC19objc_attr_NSManaged10FinalGizmoD

View File

@@ -6,14 +6,26 @@ import Foundation
// CHECK-LABEL: sil hidden @_TF25objc_attr_NSManaged_multi9testMultiFCS_10SwiftGizmoPSs9AnyObject_ : $@convention(thin) (@owned SwiftGizmo) -> @owned AnyObject {
func testMulti(obj: SwiftGizmo) -> AnyObject {
// CHECK: = class_method [volatile] %0 : $SwiftGizmo, #SwiftGizmo.kvc!1.foreign : SwiftGizmo -> () -> () , $@convention(objc_method) (SwiftGizmo) -> ()
// CHECK-NOT: return
// CHECK: = class_method [volatile] %0 : $SwiftGizmo, #SwiftGizmo.extKVC!1.foreign : SwiftGizmo -> () -> () , $@convention(objc_method) (SwiftGizmo) -> ()
// CHECK-NOT: return
// CHECK: class_method [volatile] %0 : $SwiftGizmo, #SwiftGizmo.x!getter.1.foreign : SwiftGizmo -> () -> X , $@convention(objc_method) (SwiftGizmo) -> @autoreleased X
// CHECK: return
obj.kvc()
obj.extKVC()
return obj.x
}
// CHECK-LABEL: sil hidden @_TF25objc_attr_NSManaged_multi14testFinalMultiFCS_10FinalGizmoSS : $@convention(thin) (@owned FinalGizmo) -> @owned String {
func testFinalMulti(obj: FinalGizmo) -> String {
// CHECK: class_method [volatile] %0 : $FinalGizmo, #FinalGizmo.kvc2!1.foreign : FinalGizmo -> () -> () , $@convention(objc_method) (FinalGizmo) -> ()
// CHECK-NOT: return
// CHECK: class_method [volatile] %0 : $FinalGizmo, #FinalGizmo.extKVC2!1.foreign : FinalGizmo -> () -> () , $@convention(objc_method) (FinalGizmo) -> ()
// CHECK-NOT: return
// CHECK: class_method [volatile] %0 : $FinalGizmo, #FinalGizmo.y!getter.1.foreign : FinalGizmo -> () -> String , $@convention(objc_method) (FinalGizmo) -> @autoreleased NSString
// CHECK: return
obj.kvc2()
obj.extKVC2()
return obj.y
}

View File

@@ -1,32 +0,0 @@
// RUN: %target-parse-verify-swift
protocol SwiftProto { }
@objc class C {
// okay
@NSManaged var x: Int
// expected-error@+1{{@NSManaged property cannot have an initial value}}
@NSManaged var c: C = C()
// expected-error@+1{{property cannot be marked @NSManaged because its type cannot be represented in Objective-C}}
@NSManaged var nonobjc_var: SwiftProto?
// expected-error@+1{{@NSManaged not allowed on a 'let' property}}
@NSManaged let let_property: Int = 10
// expected-error@+1{{@NSManaged not allowed on computed properties}}
@NSManaged var computed_var: Int {
return 5
}
// expected-error@+1{{@NSManaged not allowed on observing properties}}
@NSManaged var observing_var: Int {
willSet { }
}
init() {
observing_var = 10
}
}

View File

@@ -8,17 +8,37 @@ class X : NSObject {
func foo() -> X { return self }
}
@NSManaged var global: Int // expected-error {{@NSManaged only allowed on a property in a class}}
@NSManaged struct SomeStruct {} // expected-error {{@NSManaged cannot be applied to this declaration}}
@NSManaged // expected-error {{@NSManaged may only be used on 'var' declarations}}
@NSManaged var global: Int // expected-error {{@NSManaged only allowed on an instance property or method}}
@NSManaged // expected-error {{@NSManaged only allowed on an instance property or method}}
func managedFunction() {}
protocol SwiftProto { }
class SwiftGizmo : A {
@NSManaged var a: X
@NSManaged var b: Int
@NSManaged let c: Int // expected-error {{@NSManaged not allowed on a 'let' property}}
@NSManaged class var d: Int = 4 // expected-error {{@NSManaged only allowed on a property in a class}} \
// expected-error@+1{{@NSManaged property cannot have an initial value}}
@NSManaged var gizmo: SwiftGizmo = SwiftGizmo()
// expected-error@+1{{@NSManaged not allowed on computed properties}}
@NSManaged var computed_var: Int {
return 5
}
// expected-error@+1{{@NSManaged not allowed on observing properties}}
@NSManaged var observing_var: Int {
willSet { }
}
// expected-error@+1{{property cannot be marked @NSManaged because its type cannot be represented in Objective-C}}
@NSManaged var nonobjc_var: SwiftProto?
@NSManaged class var d: Int = 4 // expected-error {{@NSManaged only allowed on an instance property or method}} \
// expected-error {{class stored properties not yet supported}}
@@ -26,6 +46,10 @@ class SwiftGizmo : A {
@NSCopying @NSManaged var optionalProperty : NSString? // expected-error {{@NSManaged property cannot also be marked @NSCopying}}
@NSManaged func mutableArrayValueForA() // no-warning
@NSManaged func mutableArrayValueForB() {} // expected-error {{NSManaged method cannot have a body; it must be provided at runtime}}
@NSManaged class func mutableArrayValueForA() {} // expected-error {{@NSManaged only allowed on an instance property or method}}
override init() {}
}