Skip to content

Commit adff537

Browse files
committed
Initial support for the @NSCopying attribute on properties. This is enough to
wire it up, do basic semantic analysis and code gen a simple case of it. There is more type checking work to come, so it isn't complete yet. This is the first step to: <rdar://problem/15864836> Need a @NSCopying attribute for Cocoa types that aren't manually bridged Swift SVN r16345
1 parent 3165077 commit adff537

File tree

8 files changed

+132
-19
lines changed

8 files changed

+132
-19
lines changed

include/swift/AST/Attr.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ SIMPLE_DECL_ATTR(final, Final,
108108
OnClass | OnFunc | OnVar | OnSubscript,
109109
2)
110110

111+
SIMPLE_DECL_ATTR(NSCopying, NSCopying,
112+
OnVar, 9)
113+
114+
111115
SIMPLE_DECL_ATTR(noreturn, NoReturn,
112116
OnFunc,
113117
6)

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,16 @@ ERROR(final_not_on_accessors,sema_tcd,none,
623623
ERROR(override_noreturn_with_return,sema_tcd,none,
624624
"an override of a 'noreturn' method should also be 'noreturn'", ())
625625

626+
ERROR(nscopying_only_on_class_properties,sema_tcd,none,
627+
"'NSCopying' attribute may only be used on properties in classes",
628+
())
629+
ERROR(nscopying_only_mutable,sema_tcd,none,
630+
"'NSCopying' attribute requires property to be mutable", ())
631+
ERROR(nscopying_only_stored_property,sema_tcd,none,
632+
"'NSCopying' is only valid on stored properties", ())
633+
ERROR(nscopying_only_,sema_tcd,none,
634+
"'NSCopying' is only valid on stored properties", ())
635+
626636
//------------------------------------------------------------------------------
627637
// Type Check Expressions
628638
//------------------------------------------------------------------------------

lib/AST/Attr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ void DeclAttribute::print(ASTPrinter &Printer) const {
131131
case DAK_noreturn:
132132
Printer << "@noreturn";
133133
break;
134+
case DAK_NSCopying:
135+
Printer << "@NSCopying";
136+
break;
134137
case DAK_objc: {
135138
Printer << "@objc";
136139
llvm::SmallString<32> scratch;

lib/Parse/ParseDecl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,10 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
379379
if (!DiscardAttribute)
380380
Attributes.add(new (Context) FinalAttr(AtLoc, Loc));
381381
break;
382+
case DAK_NSCopying:
383+
if (!DiscardAttribute)
384+
Attributes.add(new (Context) NSCopyingAttr(AtLoc, Loc));
385+
break;
382386
case DAK_noreturn:
383387
if (!DiscardAttribute)
384388
Attributes.add(new (Context) NoReturnAttr(AtLoc, Loc));

lib/Sema/TypeCheckAttr.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class AttributeEarlyChecker : public AttributeVisitor<AttributeEarlyChecker> {
4343

4444
void visitFinalAttr(FinalAttr *attr) {}
4545

46+
void visitNSCopyingAttr(NSCopyingAttr *attr) {}
47+
4648
void visitNoReturnAttr(NoReturnAttr *attr) {}
4749

4850
void visitObjCAttr(ObjCAttr *attr) {}
@@ -125,6 +127,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
125127

126128
void visitFinalAttr(FinalAttr *attr);
127129

130+
void visitNSCopyingAttr(NSCopyingAttr *attr);
131+
128132
void visitNoReturnAttr(NoReturnAttr *attr);
129133

130134
void visitRequiredAttr(RequiredAttr *attr);
@@ -200,6 +204,47 @@ void AttributeChecker::visitFinalAttr(FinalAttr *attr) {
200204
}
201205
}
202206

207+
void AttributeChecker::visitNSCopyingAttr(NSCopyingAttr *attr) {
208+
// The @NSCopying attribute is only allowed on stored properties.
209+
auto *VD = dyn_cast<VarDecl>(D);
210+
if (!VD) {
211+
TC.diagnose(attr->getLocation(), diag::nscopying_only_on_class_properties);
212+
attr->setInvalid();
213+
return;
214+
}
215+
216+
// It may only be used on class members.
217+
auto typeContext = D->getDeclContext()->getDeclaredTypeInContext();
218+
auto contextTypeDecl =
219+
typeContext ? typeContext->getNominalOrBoundGenericNominal() : nullptr;
220+
if (!contextTypeDecl || !isa<ClassDecl>(contextTypeDecl)) {
221+
TC.diagnose(attr->getLocation(), diag::nscopying_only_on_class_properties);
222+
attr->setInvalid();
223+
return;
224+
}
225+
226+
if (!VD->isSettable(VD->getDeclContext())) {
227+
TC.diagnose(attr->getLocation(), diag::nscopying_only_mutable);
228+
attr->setInvalid();
229+
return;
230+
}
231+
232+
if (!VD->hasStorage()) {
233+
TC.diagnose(attr->getLocation(), diag::nscopying_only_stored_property);
234+
attr->setInvalid();
235+
return;
236+
}
237+
238+
assert(VD->getOverriddenDecl() == nullptr &&
239+
"Can't have value with storage that is an override");
240+
241+
// Check the type. It must be must be [unchecked]optional, weak, a normal
242+
// class, AnyObject, or classbound protocol.
243+
// must conform to the NSCopying protocol.
244+
245+
}
246+
247+
203248
void AttributeChecker::visitNoReturnAttr(NoReturnAttr *attr) {
204249
auto *FD = dyn_cast<FuncDecl>(D);
205250
if (!FD) {

lib/Sema/TypeCheckDecl.cpp

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,13 +1387,54 @@ static Expr *createPropertyLoadOrCallSuperclassGetter(VarDecl *VD,
13871387
SourceLoc(), /*implicit*/true);
13881388
}
13891389

1390+
1391+
/// Synthesize the code to store 'Val' to 'VD', given that VD has an @NSCopying
1392+
/// attribute on it. We know that VD is a stored property in a class, so we
1393+
/// just need to generate something like "self.property = val.copyWithZone(nil)"
1394+
/// here. This does some type checking to validate that the call will succeed.
1395+
static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD, TypeChecker &TC) {
1396+
auto &Ctx = TC.Context;
1397+
1398+
// Generate:
1399+
// (force_value_expr type='<null>'
1400+
// (call_expr type='<null>'
1401+
// (unresolved_dot_expr type='<null>' field 'copyWithZone'
1402+
// "Val")
1403+
// (paren_expr type='<null>'
1404+
// (unresolved_decl_ref_expr type='<null>' name=nil specialized=no))))
1405+
auto UDE = new (Ctx) UnresolvedDotExpr(Val, SourceLoc(),
1406+
Ctx.getIdentifier("copyWithZone"),
1407+
SourceLoc(), /*implicit*/true);
1408+
Expr *Nil = new (Ctx) UnresolvedDeclRefExpr(Ctx.getIdentifier("nil"),
1409+
DeclRefKind::Ordinary,
1410+
SourceLoc());
1411+
Nil = new (Ctx) ParenExpr(SourceLoc(), Nil, SourceLoc(), false);
1412+
1413+
//- (id)copyWithZone:(NSZone *)zone;
1414+
Expr *Call = new (Ctx) CallExpr(UDE, Nil, /*implicit*/true);
1415+
1416+
TypeLoc ResultTy;
1417+
ResultTy.setType(VD->getType(), true);
1418+
1419+
Call = new (Ctx) ConditionalCheckedCastExpr(Call, SourceLoc(),
1420+
TypeLoc::withoutLoc(VD->getType()));
1421+
Call->setImplicit();
1422+
return new (Ctx) ForceValueExpr(Call, SourceLoc());
1423+
}
1424+
13901425
/// Store 'Val' to 'VD'. If VD is an @override of another value, we call the
13911426
/// superclass setter. Otherwise, we do a direct store of the value.
1392-
static Expr *createPropertyStoreOrCallSuperclassSetter(Expr *Val,
1393-
VarDecl *VD,
1394-
VarDecl *SelfDecl) {
1427+
static void createPropertyStoreOrCallSuperclassSetter(Expr *Val, VarDecl *VD,
1428+
VarDecl *SelfDecl,
1429+
SmallVectorImpl<ASTNode> &Result,
1430+
TypeChecker &TC) {
13951431
auto &Ctx = VD->getASTContext();
13961432

1433+
// If this property is @NSCopying, then we store the result of a copyWithZone
1434+
// call on the value, not the value itself.
1435+
if (VD->getAttrs().hasAttribute<NSCopyingAttr>())
1436+
Val = synthesizeCopyWithZoneCall(Val, VD, TC);
1437+
13971438
// Create:
13981439
// (assign (decl_ref_expr(VD)), decl_ref_expr(value))
13991440
// or:
@@ -1411,8 +1452,7 @@ static Expr *createPropertyStoreOrCallSuperclassSetter(Expr *Val,
14111452
Dest = new (Ctx) UnresolvedDotExpr(SRE, SourceLoc(), VD->getName(),
14121453
SourceLoc(), /*implicit*/true);
14131454
}
1414-
return new (Ctx) AssignExpr(Dest, SourceLoc(), Val, true);
1415-
1455+
Result.push_back(new (Ctx) AssignExpr(Dest, SourceLoc(), Val, true));
14161456
}
14171457

14181458

@@ -1445,7 +1485,7 @@ static void synthesizeTrivialGetter(FuncDecl *Get, VarDecl *VD) {
14451485
/// Given a "Stored" property that needs to be converted to
14461486
/// StoredWithTrivialAccessors, create the trivial getter and setter, and switch
14471487
/// the storage kind.
1448-
static void addTrivialAccessorsToStoredVar(VarDecl *VD, TypeChecker &TC) {
1488+
static void addAccessorsToStoredVar(VarDecl *VD, TypeChecker &TC) {
14491489
assert(VD->getStorageKind() == VarDecl::Stored && "Isn't a stored vardecl");
14501490
auto &Context = VD->getASTContext();
14511491
SourceLoc Loc = VD->getLoc();
@@ -1463,9 +1503,10 @@ static void addTrivialAccessorsToStoredVar(VarDecl *VD, TypeChecker &TC) {
14631503
VarDecl *SelfDecl = Set->getImplicitSelfDecl();
14641504

14651505
auto *ValueDRE = new (Context) DeclRefExpr(ValueDecl, SourceLoc(), true);
1466-
ASTNode Assign = createPropertyStoreOrCallSuperclassSetter(ValueDRE, VD,
1467-
SelfDecl);
1468-
Set->setBody(BraceStmt::create(Context, Loc, Assign, Loc));
1506+
SmallVector<ASTNode, 1> SetterBody;
1507+
createPropertyStoreOrCallSuperclassSetter(ValueDRE, VD, SelfDecl,
1508+
SetterBody, TC);
1509+
Set->setBody(BraceStmt::create(Context, Loc, SetterBody, Loc));
14691510

14701511
// Mark it transparent, there is no user benefit to this actually existing.
14711512
Set->getMutableAttrs().setAttr(AK_transparent, Loc);
@@ -1503,12 +1544,14 @@ static void addTrivialAccessorsToStoredVar(VarDecl *VD, TypeChecker &TC) {
15031544
appendMembers(SD, newMembers);
15041545
}
15051546

1547+
1548+
15061549
/// The specified VarDecl with "Stored" StorageKind was just found to satisfy
15071550
/// a protocol property requirement. Convert it to
15081551
/// "StoredWithTrivialAccessors" storage by sythesizing accessors for the
15091552
/// variable, enabling the witness table to use those accessors.
15101553
void TypeChecker::synthesizeWitnessAccessorsForStoredVar(VarDecl *VD) {
1511-
addTrivialAccessorsToStoredVar(VD, *this);
1554+
addAccessorsToStoredVar(VD, *this);
15121555

15131556
// Type check the body of the getter and setter.
15141557
validateDecl(VD->getGetter(), true);
@@ -1523,7 +1566,7 @@ void TypeChecker::synthesizeWitnessAccessorsForStoredVar(VarDecl *VD) {
15231566

15241567
/// Given a VarDecl with a willSet: and/or didSet: specifier, synthesize the
15251568
/// (trivial) getter and the setter, which calls these.
1526-
static void synthesizeObservingAccessors(VarDecl *VD) {
1569+
static void synthesizeObservingAccessors(VarDecl *VD, TypeChecker &TC) {
15271570
assert(VD->getStorageKind() == VarDecl::Observing);
15281571
assert(VD->getGetter() && VD->getSetter() &&
15291572
!VD->getGetter()->hasBody() && !VD->getSetter()->hasBody() &&
@@ -1598,10 +1641,9 @@ static void synthesizeObservingAccessors(VarDecl *VD) {
15981641

15991642
// Create an assignment into the storage or call to superclass setter.
16001643
auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, SourceLoc(), true);
1601-
ASTNode Assign = createPropertyStoreOrCallSuperclassSetter(ValueDRE, VD,
1602-
SelfDecl);
1603-
SetterBody.push_back(Assign);
1604-
1644+
createPropertyStoreOrCallSuperclassSetter(ValueDRE, VD, SelfDecl, SetterBody,
1645+
TC);
1646+
16051647
// Create:
16061648
// (call_expr (dot_syntax_call_expr (decl_ref_expr(didSet)),
16071649
// (decl_ref_expr(self))),
@@ -1898,7 +1940,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
18981940
// and setter accessors and change its storage kind. This allows it to be
18991941
// overriden and provide objc entrypoints if needed.
19001942
if (VD->getStorageKind() == VarDecl::Stored &&
1901-
!VD->isStatic() && !VD->isFinal()) {
1943+
!VD->isStatic()) {
19021944

19031945
bool isClassMember = false;
19041946
if (auto ctx = VD->getDeclContext()->getDeclaredTypeOfContext())
@@ -1909,8 +1951,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
19091951
if (auto sourceFile = VD->getDeclContext()->getParentSourceFile())
19101952
isInSILMode = sourceFile->Kind == SourceFileKind::SIL;
19111953

1912-
if (isClassMember && !isInSILMode) {
1913-
addTrivialAccessorsToStoredVar(VD, TC);
1954+
if (isClassMember && !isInSILMode &&
1955+
// FIXME: Shouldn't be checking for @final here.
1956+
(!VD->isFinal() || VD->getAttrs().hasAttribute<NSCopyingAttr>())) {
1957+
addAccessorsToStoredVar(VD, TC);
19141958

19151959
// Type check the body of the getter and setter.
19161960
TC.typeCheckDecl(VD->getGetter(), true);
@@ -1926,7 +1970,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
19261970
// decl.
19271971
if (VD->getStorageKind() == VarDecl::Observing &&
19281972
!VD->getGetter()->getBody()) {
1929-
synthesizeObservingAccessors(VD);
1973+
synthesizeObservingAccessors(VD, TC);
19301974

19311975
// Type check the body of the getter and setter.
19321976
TC.typeCheckDecl(VD->getGetter(), true);
@@ -3487,6 +3531,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
34873531
UNINTERESTING_ATTR(Exported)
34883532
UNINTERESTING_ATTR(Override)
34893533
UNINTERESTING_ATTR(Required)
3534+
UNINTERESTING_ATTR(NSCopying)
34903535

34913536
#undef UNINTERESTING_ATTR
34923537

test/SILGen/Inputs/usr/include/BridgeTestFoundation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ typedef signed char BOOL;
1616
@interface NSString : NSObject
1717

1818
- (NSString*)uppercaseString;
19+
- (id) copyWithZone: (void*)zone;
1920

2021
@end
2122

test/SILGen/Inputs/usr/include/Gizmo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ typedef long NSInteger;
5858
@end
5959

6060
@interface NSString : NSObject
61+
- (id) copyWithZone: (void*)zone;
6162
@end
6263

6364
@interface NSView : NSObject

0 commit comments

Comments
 (0)