diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index a5871efcb2fa4..4fc7970777d07 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1962,7 +1962,16 @@ ERROR(extension_metatype,none, ERROR(extension_placeholder,none, "cannot extend a type that contains placeholders", ()) ERROR(extension_stored_property,none, - "extensions must not contain stored properties", ()) + "stored property is not permitted in extension", ()) +ERROR(extension_stored_property_in_constrained_extension,none, + "stored property is not permitted in constrained extension", ()) +ERROR(extension_stored_property_on_protocol,none, + "stored property is not permitted in extension of protocol", ()) +ERROR(extension_stored_property_on_enum,none, + "stored property is not permitted in extension of enum", ()) +ERROR(extension_stored_property_in_different_file,none, + "only extensions defined in the same file as the extended type " + "are permitted to define stored properties", ()) NOTE(extension_stored_property_fixit,none, "Remove '=' to make %0 a computed property", (Identifier)) ERROR(extension_nongeneric_trailing_where,none, @@ -6206,7 +6215,7 @@ ERROR(property_wrapper_not_single_var, none, "property wrapper can only apply to a single variable", ()) ERROR(property_with_wrapper_in_bad_context,none, "%select{|non-static|non-static}1 %2 %0 declared inside " - "%select{a protocol|an extension|an enum}1 cannot have a wrapper", + "%select{a protocol|an enum}1 cannot have a wrapper", (Identifier, int, DescriptiveDeclKind)) ERROR(property_with_wrapper_overrides,none, "property %0 with attached wrapper cannot override another property", diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 98208104ca4a7..68b420f87cbec 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -3697,7 +3697,7 @@ class Verifier : public ASTWalker { } else { llvm_unreachable("impossible parent node"); } - + if (!Ctx.SourceMgr.rangeContains(Enclosing, Current)) { Out << "child source range not contained within its parent: "; printEntity(); diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index ef39be2b1acd5..330ea5cf077fb 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -991,7 +991,12 @@ namespace { } SILType getType(VarDecl *field) { - assert(field->getDeclContext() == TheStruct->getAnyNominal()); + auto DC = field->getDeclContext(); + assert(DC == TheStruct->getAnyNominal() + // Stored properties in extensions within the same file are added + // as members to the defining type, so we shouldn't assert in that + // case + || DC->getContextKind() == DeclContextKind::ExtensionDecl); auto silType = SILType::getPrimitiveAddressType(TheStruct); return silType.getFieldType( field, IGM.getSILModule(), diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 19ead1142448a..1e4abc969c3fd 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -563,8 +563,13 @@ SILLinkage SILDeclRef::getDefinitionLinkage() const { // Stored property initializers have linkage based on the access level of // their nominal. - if (isStoredPropertyInitializer()) - decl = cast(decl->getDeclContext()); + if (isStoredPropertyInitializer()) { + if (auto extension = dyn_cast(decl->getDeclContext())) { + decl = extension->getExtendedNominal(); + } else { + decl = cast(decl->getDeclContext()); + } + } // Compute the effective access level, taking e.g testable into consideration. auto effectiveAccess = decl->getEffectiveAccess(); @@ -790,7 +795,13 @@ IsSerialized_t SILDeclRef::isSerialized() const { // marked as @frozen. if (isStoredPropertyInitializer() || (isPropertyWrapperBackingInitializer() && d->getDeclContext()->isTypeContext())) { - auto *nominal = cast(d->getDeclContext()); + NominalTypeDecl *nominal; + if (auto extension = dyn_cast(d->getDeclContext())) { + nominal = extension->getExtendedNominal(); + } else { + nominal = cast(d->getDeclContext()); + } + auto scope = nominal->getFormalAccessScope(/*useDC=*/nullptr, /*treatUsableFromInlineAsPublic=*/true); diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 2fcbfd64c7815..e9b6dd7894a8d 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -3264,7 +3264,12 @@ class SILVerifier : public SILVerifierBase { require(EI->getField()->hasStorage(), "cannot get address of computed property with struct_element_addr"); - require(EI->getField()->getDeclContext() == sd, + auto DC = EI->getField()->getDeclContext(); + require(DC == sd + // Stored properties in extensions within the same file are + // added as members to the defining type, so we shouldn't assert + // in that case + || DC->getContextKind() == DeclContextKind::ExtensionDecl, "struct_element_addr field is not a member of the struct"); if (EI->getModule().getStage() != SILStage::Lowered) { diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index dd7e56d2c68f6..5d987aa8152c4 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1061,11 +1061,18 @@ emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, Pattern *pattern) { selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext()); SILValue slot; - if (auto *structDecl = dyn_cast(field->getDeclContext())) { + NominalTypeDecl *nominal; + if (auto extension = dyn_cast(field->getDeclContext())) { + nominal = extension->getExtendedNominal(); + } else { + nominal = cast(field->getDeclContext()); + } + + if (auto *structDecl = dyn_cast(nominal)) { slot = SGF.B.createStructElementAddr(pattern, self.forward(SGF), field, fieldTy.getAddressType()); } else { - assert(isa(field->getDeclContext())); + assert(isa(nominal)); slot = SGF.B.createRefElementAddr(pattern, self.forward(SGF), field, fieldTy.getAddressType()); } @@ -1162,73 +1169,95 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, NominalTypeDecl *nominal) { auto subs = getSubstitutionsForPropertyInitializer(dc, nominal); + // open question: is there a good "ordered set" type we could use + // here instead of a parallel array / unordered set? + llvm::SmallVector patternDecls; + llvm::SmallDenseSet seenPatterns; + for (auto member : nominal->getMembers()) { - // Find instance pattern binding declarations that have initializers. if (auto pbd = dyn_cast(member)) { - if (pbd->isStatic()) continue; - - for (auto i : range(pbd->getNumPatternEntries())) { - auto init = pbd->getExecutableInit(i); - if (!init) continue; + patternDecls.push_back(pbd); + seenPatterns.insert(pbd); + } + } - auto *varPattern = pbd->getPattern(i); + // A type can also have stored properties that aren't direct members, + // e.g. if they were defined in an extension. + for (auto storedProperty : nominal->getStoredProperties()) { + if (auto pbd = storedProperty->getParentPatternBinding()) { + if (!seenPatterns.count(pbd)) { + patternDecls.push_back(pbd); + seenPatterns.insert(pbd); + } + } + } - // Cleanup after this initialization. - FullExpr scope(Cleanups, varPattern); - - // Get the type of the initialization result, in terms - // of the constructor context's archetypes. - auto resultType = getInitializationTypeInContext( - pbd->getDeclContext(), dc, varPattern); - AbstractionPattern origType = resultType.first; - CanType substType = resultType.second; - - // Figure out what we're initializing. - auto memberInit = emitMemberInit(*this, selfDecl, varPattern); - - // This whole conversion thing is about eliminating the - // paired orig-to-subst subst-to-orig conversions that - // will happen if the storage is at a different abstraction - // level than the constructor. When emitApply() is used - // to call the stored property initializer, it naturally - // wants to convert the result back to the most substituted - // abstraction level. To undo this, we use a converting - // initialization and rely on the peephole that optimizes - // out the redundant conversion. - SILType loweredResultTy; - SILType loweredSubstTy; - - // A converting initialization isn't necessary if the member is - // a property wrapper. Though the initial value can have a - // reabstractable type, the result of the initialization is - // always the property wrapper type, which is never reabstractable. - bool needsConvertingInit = false; - auto *singleVar = varPattern->getSingleVar(); - if (!(singleVar && singleVar->getOriginalWrappedProperty())) { - loweredResultTy = getLoweredType(origType, substType); - loweredSubstTy = getLoweredType(substType); - needsConvertingInit = loweredResultTy != loweredSubstTy; - } + for (auto pbd : patternDecls) { + // Find instance pattern binding declarations that have initializers. + if (pbd->isStatic()) + continue; + + for (auto i : range(pbd->getNumPatternEntries())) { + auto init = pbd->getExecutableInit(i); + if (!init) + continue; + + auto *varPattern = pbd->getPattern(i); + + // Cleanup after this initialization. + FullExpr scope(Cleanups, varPattern); + + // Get the type of the initialization result, in terms + // of the constructor context's archetypes. + auto resultType = + getInitializationTypeInContext(pbd->getDeclContext(), dc, varPattern); + AbstractionPattern origType = resultType.first; + CanType substType = resultType.second; + + // Figure out what we're initializing. + auto memberInit = emitMemberInit(*this, selfDecl, varPattern); + + // This whole conversion thing is about eliminating the + // paired orig-to-subst subst-to-orig conversions that + // will happen if the storage is at a different abstraction + // level than the constructor. When emitApply() is used + // to call the stored property initializer, it naturally + // wants to convert the result back to the most substituted + // abstraction level. To undo this, we use a converting + // initialization and rely on the peephole that optimizes + // out the redundant conversion. + SILType loweredResultTy; + SILType loweredSubstTy; + + // A converting initialization isn't necessary if the member is + // a property wrapper. Though the initial value can have a + // reabstractable type, the result of the initialization is + // always the property wrapper type, which is never reabstractable. + bool needsConvertingInit = false; + auto *singleVar = varPattern->getSingleVar(); + if (!(singleVar && singleVar->getOriginalWrappedProperty())) { + loweredResultTy = getLoweredType(origType, substType); + loweredSubstTy = getLoweredType(substType); + needsConvertingInit = loweredResultTy != loweredSubstTy; + } - if (needsConvertingInit) { - Conversion conversion = Conversion::getSubstToOrig( - origType, substType, - loweredResultTy); + if (needsConvertingInit) { + Conversion conversion = + Conversion::getSubstToOrig(origType, substType, loweredResultTy); - ConvertingInitialization convertingInit(conversion, - SGFContext(memberInit.get())); + ConvertingInitialization convertingInit(conversion, + SGFContext(memberInit.get())); - emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, - origType, substType, &convertingInit); + emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, origType, + substType, &convertingInit); - auto finalValue = convertingInit.finishEmission( - *this, varPattern, ManagedValue::forInContext()); - if (!finalValue.isInContext()) - finalValue.forwardInto(*this, varPattern, memberInit.get()); - } else { - emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, - origType, substType, memberInit.get()); - } + auto finalValue = convertingInit.finishEmission( + *this, varPattern, ManagedValue::forInContext()); + if (!finalValue.isInContext()) + finalValue.forwardInto(*this, varPattern, memberInit.get()); + } else { + emitAndStoreInitialValueInto(*this, varPattern, pbd, i, subs, origType, + substType, memberInit.get()); } } } diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 66e74040e3f24..0bf39f82cde95 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1076,6 +1076,7 @@ class SILGenType : public TypeMemberVisitor { public: SILGenModule &SGM; NominalTypeDecl *theType; + llvm::SmallDenseSet visitedVarDecls; SILGenType(SILGenModule &SGM, NominalTypeDecl *theType) : SGM(SGM), theType(theType) {} @@ -1087,6 +1088,14 @@ class SILGenType : public TypeMemberVisitor { for (Decl *member : theType->getABIMembers()) visit(member); + // Types can have stored properties that aren't included in + // `getABIMembers()`, so we have to handle those manually + for (VarDecl *VD : theType->getStoredProperties()) { + if (!visitedVarDecls.count(VD)) { + visit(VD); + } + } + // Build a vtable if this is a class. if (auto theClass = dyn_cast(theType)) { SILGenVTable genVTable(SGM, theClass); @@ -1176,6 +1185,8 @@ class SILGenType : public TypeMemberVisitor { } void visitVarDecl(VarDecl *vd) { + visitedVarDecls.insert(vd); + // Collect global variables for static properties. // FIXME: We can't statically emit a global variable for generic properties. if (vd->isStatic() && vd->hasStorage()) { @@ -1305,7 +1316,13 @@ class SILGenExtension : public TypeMemberVisitor { // Emit initializers for static variables. for (auto i : range(pd->getNumPatternEntries())) { if (pd->getExecutableInit(i)) { - assert(pd->isStatic() && "stored property in extension?!"); + // Ignore any stored properties, since at codegen time + // these are handled as if they were defined in the type + // being extended rather than the extension itself + if (!pd->isStatic()) { + continue; + } + SGM.emitGlobalInitialization(pd, i); } } @@ -1315,8 +1332,14 @@ class SILGenExtension : public TypeMemberVisitor { if (vd->hasStorage()) { bool hasDidSetOrWillSetDynamicReplacement = vd->hasDidSetOrWillSetDynamicReplacement(); - assert((vd->isStatic() || hasDidSetOrWillSetDynamicReplacement) && - "stored property in extension?!"); + + // Ignore any stored properties, since at codegen time + // these are handled as if they were defined in the type + // being extended rather than the extension itself + if (!(vd->isStatic() || hasDidSetOrWillSetDynamicReplacement)) { + return; + } + if (!hasDidSetOrWillSetDynamicReplacement) { emitTypeMemberGlobalVariable(SGM, vd); visitAccessors(vd); diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index fb072f5e72976..c3afcdf138807 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -339,11 +339,28 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, if (ICK == ImplicitConstructorKind::Memberwise) { assert(isa(decl) && "Only struct have memberwise constructor"); + // open question: is there a good "ordered set" type we could use + // here instead of a parallel array / unordered set? + llvm::SmallVector varDecls; + llvm::SmallDenseSet seenDecls; + for (auto member : decl->getMembers()) { - auto var = dyn_cast(member); - if (!var) - continue; + if (auto var = dyn_cast(member)) { + varDecls.push_back(var); + seenDecls.insert(var); + } + } + // A type can also have stored properties that aren't direct members, + // e.g. if they were defined in an extension. + for (auto storedDecl : decl->getStoredProperties()) { + if (!seenDecls.count(storedDecl)) { + varDecls.push_back(storedDecl); + seenDecls.insert(storedDecl); + } + } + + for (auto var : varDecls) { if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) continue; @@ -1396,6 +1413,15 @@ HasMemberwiseInitRequest::evaluate(Evaluator &evaluator, return true; } } + + // Also check the list of stored properties, which may include + // properties defined in extensions and not included as direct members + for (auto var : decl->getStoredProperties()) { + if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) { + return true; + } + } + return false; } diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index abeb31c791e8e..55f3754d424a8 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -517,18 +517,14 @@ AttachedPropertyWrappersRequest::evaluate(Evaluator &evaluator, dc = dc->getAsDecl()->getDeclContext(); } - // A property with a wrapper cannot be declared in a protocol, enum, or - // an extension. + // A property with a wrapper cannot be declared in a protocol or enum if (isa(dc) || - (isa(dc) && var->isInstanceMember()) || (isa(dc) && var->isInstanceMember())) { int whichKind; if (isa(dc)) whichKind = 0; - else if (isa(dc)) - whichKind = 1; else - whichKind = 2; + whichKind = 1; var->diagnose(diag::property_with_wrapper_in_bad_context, var->getName(), whichKind, var->getDescriptiveKind()) .highlight(attr->getRange()); diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 6440b505f35cb..55b8899ca48e1 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -207,6 +207,28 @@ static void enumerateStoredPropertiesAndMissing( if (missing->getNumberOfFieldOffsetVectorEntries() > 0) addMissing(missing); } + + // Extensions within the same file as this NominalTypeDecl can define + // stored properties for this type + if (auto declFile = dyn_cast(decl->getModuleScopeContext())) { + for (auto extension : decl->getExtensions()) { + auto extensionFile = dyn_cast(decl->getModuleScopeContext()); + if (!extensionFile || extensionFile != declFile) { + continue; + } + + for (auto member : extension->getMembers()) { + if (auto *var = dyn_cast(member)) { + if (!var->isStatic() && + var->hasStorage() + // @_dynamicReplacament properties are handled elsewhere + && !var->getAttrs().getAttribute()) { + addStoredProperty(var); + } + } + } + } + } } ArrayRef @@ -3325,11 +3347,49 @@ static void finishStorageImplInfo(AbstractStorageDecl *storage, info = StorageImplInfo::getMutableComputed(); } else if (isa(dc) && !storage->getAttrs().getAttribute()) { - storage->diagnose(diag::extension_stored_property); - info = (info.supportsMutation() - ? StorageImplInfo::getMutableComputed() - : StorageImplInfo::getImmutableComputed()); + // VarDecls with storage are permitted to be defined in unconstrained + // extensions on concrete types (but not protocols or enums) as long as + // the extension is in the same file as the extended type + bool extensionInSameFileAsType = false; + bool extendsProtocol = false; + bool extendsEnum = false; + auto ext = dyn_cast(dc); + if (auto type = ext->getExtendedNominal()) { + auto typeFile = dyn_cast(type->getModuleScopeContext()); + auto extFile = dyn_cast(ext->getModuleScopeContext()); + extensionInSameFileAsType = typeFile && extFile && typeFile == extFile; + extendsProtocol = isa(type); + extendsEnum = isa(type); + } + + bool storedPropertyIsPermitted = + isa(storage) && extensionInSameFileAsType && + !ext->isConstrainedExtension() && !extendsProtocol && !extendsEnum; + + if (!storedPropertyIsPermitted) { + // Tailor the diagnostic to match the specific reason this decl + // is not permitted + if (isa(storage)) { + if (extendsProtocol) { + storage->diagnose(diag::extension_stored_property_on_protocol); + } else if (extendsEnum) { + storage->diagnose(diag::extension_stored_property_on_enum); + } else if (ext->isConstrainedExtension()) { + storage->diagnose( + diag::extension_stored_property_in_constrained_extension); + } else { + storage->diagnose( + diag::extension_stored_property_in_different_file); + } + } else { + storage->diagnose(diag::extension_stored_property); + } + + info = + (info.supportsMutation() ? StorageImplInfo::getMutableComputed() + : StorageImplInfo::getImmutableComputed()); + } } } } diff --git a/test/NameLookup/name_lookup.swift b/test/NameLookup/name_lookup.swift index 11db22b1bf924..3303a976c5e65 100644 --- a/test/NameLookup/name_lookup.swift +++ b/test/NameLookup/name_lookup.swift @@ -369,7 +369,7 @@ extension ThisBase1 { func baseExtFunc0() {} - var baseExtStaticVar: Int // expected-error {{extensions must not contain stored properties}} // expected-note 2 {{'baseExtStaticVar' declared here}} + var baseExtStaticVar: Int = 42 // expected-note 2 {{'baseExtStaticVar' declared here}} var baseExtStaticProp: Int { // expected-note 2 {{'baseExtStaticProp' declared here}} get { @@ -401,7 +401,7 @@ extension ThisDerived1 { func derivedExtFunc0() {} - var derivedExtStaticVar: Int // expected-error {{extensions must not contain stored properties}} + var derivedExtStaticVar: Int = 42 var derivedExtStaticProp: Int { get { diff --git a/test/decl/ext/generic.swift b/test/decl/ext/generic.swift index 753f87b2131c4..68f55d9446469 100644 --- a/test/decl/ext/generic.swift +++ b/test/decl/ext/generic.swift @@ -20,12 +20,22 @@ extension Double : P2 { extension X { let x = 0 - // expected-error@-1 {{extensions must not contain stored properties}} + // expected-error@-1 {{stored property is not permitted in constrained extension}} static let x = 0 func f() -> Int {} class C {} } +extension X { + let t: T + let u: U + let v: V + + static func makeX(t: T, u: U, v: V) -> X { + X(t: t, u: u, v: v) + } +} + typealias GGG = X extension GGG { } // okay through a typealias diff --git a/test/decl/func/constructor.swift b/test/decl/func/constructor.swift index 29e373c33a5ba..e7170df5d12c2 100644 --- a/test/decl/func/constructor.swift +++ b/test/decl/func/constructor.swift @@ -108,3 +108,39 @@ var noCrash1c : NoCrash1a class MissingDef { init() // expected-error{{initializer requires a body}} } + +// Stored properties in extensions appear in implicit constructor +struct StructWithExtension { } + +extension StructWithExtension { + let t: String + let u: Int + let v: Double +} + +_ = StructWithExtension(t: "T", u: 42, v: 1.0) + +class ClassWithExtension { + init(t: String) { + self.t = t + } +} + +extension ClassWithExtension { + let t: String +} + +_ = ClassWithExtension(t: "T") + +class SubclassWithExtendedSuperclass: ClassWithExtension { + init(t: String, u: String) { + self.u = u + super.init(t: t) + } +} + +extension SubclassWithExtendedSuperclass { + let u: String +} + +_ = SubclassWithExtendedSuperclass(t: "T", u: "U") \ No newline at end of file diff --git a/test/decl/protocol/ownership_protocol.swift b/test/decl/protocol/ownership_protocol.swift index 2fa4daca1b9ac..607fef6f729f3 100644 --- a/test/decl/protocol/ownership_protocol.swift +++ b/test/decl/protocol/ownership_protocol.swift @@ -17,8 +17,8 @@ protocol P { } extension P { - weak var foo5: SomeClass? // expected-error {{extensions must not contain stored properties}} - unowned var foo6: SomeClass // expected-error {{extensions must not contain stored properties}} + weak var foo5: SomeClass? // expected-error {{stored property is not permitted in extension of protocol}} + unowned var foo6: SomeClass // expected-error {{stored property is not permitted in extension of protocol}} weak var foo7: SomeClass? { // Okay return SomeClass() diff --git a/test/decl/var/properties.swift b/test/decl/var/properties.swift index 4ff98e2dd742a..c5906f2bbaf06 100644 --- a/test/decl/var/properties.swift +++ b/test/decl/var/properties.swift @@ -448,11 +448,11 @@ extension S { } struct StructWithExtension1 { - var foo: Int + var foo: Int = 42 static var fooStatic = 4 } extension StructWithExtension1 { - var fooExt: Int // expected-error {{extensions must not contain stored properties}} + var fooExt: Int static var fooExtStatic = 4 } @@ -461,7 +461,7 @@ class ClassWithExtension1 { class var fooStatic = 4 // expected-error {{class stored properties not supported in classes; did you mean 'static'?}} } extension ClassWithExtension1 { - var fooExt: Int // expected-error {{extensions must not contain stored properties}} + var fooExt: Int = 42 class var fooExtStatic = 4 // expected-error {{class stored properties not supported in classes; did you mean 'static'?}} } @@ -470,7 +470,7 @@ enum EnumWithExtension1 { static var fooStatic = 4 } extension EnumWithExtension1 { - var fooExt: Int // expected-error {{extensions must not contain stored properties}} + var fooExt: Int // expected-error {{stored property is not permitted in extension of enum}} static var fooExtStatic = 4 } @@ -479,7 +479,7 @@ protocol ProtocolWithExtension1 { static var fooStatic : Int { get } } extension ProtocolWithExtension1 { - var fooExt: Int // expected-error{{extensions must not contain stored properties}} + var fooExt: Int // expected-error{{stored property is not permitted in extension of protocol}} static var fooExtStatic = 4 // expected-error{{static stored properties not supported in protocol extensions}} } @@ -1154,8 +1154,8 @@ class rdar17391625 { } extension rdar17391625 { - var someStoredVar: Int // expected-error {{extensions must not contain stored properties}} - var someObservedVar: Int { // expected-error {{extensions must not contain stored properties}} + var someStoredVar: Int = 42 + var someObservedVar: Int { didSet { } } @@ -1177,7 +1177,7 @@ extension rdar17391625derived { public protocol rdar27671033P {} struct rdar27671033S {} extension rdar27671033S : rdar27671033P where Key == String { - let d = rdar27671033S() // expected-error {{extensions must not contain stored properties}} + let d = rdar27671033S() // expected-error {{stored property is not permitted in constrained extension}} } // struct memberwise initializer violates new sanctity of previously set `let` property @@ -1313,7 +1313,7 @@ class C_53385 { class C1_51744 {} extension C1_51744 { - var foo: String = { // expected-error {{extensions must not contain stored properties}} // expected-error {{function produces expected type 'String'; did you mean to call it with '()'?}} // expected-note {{Remove '=' to make 'foo' a computed property}}{{19-21=}} + var foo: String = { // expected-error {{function produces expected type 'String'; did you mean to call it with '()'?}} // expected-note {{Remove '=' to make 'foo' a computed property}}{{19-21=}} return "Hello" } } @@ -1324,6 +1324,10 @@ enum E_51744 { } } +extension E_51744 { + var storedPropertyInEnumExtension: Int = 42 // expected-error {{stored property is not permitted in extension of enum}} +} + var v_51744: Int = { // expected-error {{function produces expected type 'Int'; did you mean to call it with '()'?}} // expected-note {{Remove '=' to make 'v_51744' a computed property}}{{18-20=}} return 0 } @@ -1352,3 +1356,7 @@ enum E1_57936 { enum E2_57936 { var foo: T {} // expected-error{{missing return in accessor expected to return 'T'}} } + +extension String { + let storedPropertyInStringExtension: Int = 42 // expected-error {{only extensions defined in the same file as the extended type are permitted to define stored properties}} +} \ No newline at end of file diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 2472e6a2eaea6..ecca60745af25 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -203,11 +203,14 @@ struct HasWrapper { } extension HasWrapper { @Wrapper(stored: 17) - var inExt: Int // expected-error{{property 'inExt' declared inside an extension cannot have a wrapper}} - // expected-error@-1{{extensions must not contain stored properties}} + var inExt: Int @Wrapper(stored: 17) static var x: Int + + static func makeHasWrapper() -> HasWrapper { + HasWrapper(inExt: Wrapper(stored: 42)) + } } class ClassWithWrappers { diff --git a/test/diagnostics/pretty-printed-diagnostics.swift b/test/diagnostics/pretty-printed-diagnostics.swift index 6ff1e65045cd1..66a6f3a62d06f 100644 --- a/test/diagnostics/pretty-printed-diagnostics.swift +++ b/test/diagnostics/pretty-printed-diagnostics.swift @@ -108,22 +108,6 @@ foo(b: // CHECK: | ^ note: did you mean to disable error propagation? [insert 'try! '] // CHECK: [[#LINE+1]] | - -// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:7 -// CHECK: [[#LINE-1]] | extension A { -// CHECK: [[#LINE]] | let x: Int = { 42 } -// CHECK: | ^ error: extensions must not contain stored properties -// CHECK: [[#LINE+1]] | } - -// Test complex out-of-line fix-its. -// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:16 -// CHECK: [[#LINE-1]] | extension A { -// CHECK: [[#LINE]] | let x: Int = { 42 }() -// CHECK: | ~~~~~~++ -// CHECK: | ^ error: function produces expected type 'Int'; did you mean to call it with '()'? -// CHECK: | ^ note: Remove '=' to make 'x' a computed property [remove '= ' and replace 'let' with 'var'] -// CHECK: [[#LINE+1]] | } - // CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:8 // CHECK: [[#LINE-1]] | // CHECK: [[#LINE]] | struct B: Decodable {