Skip to content

Fix objcImpl bug causing invalid @_hasStorage attributes in module interfaces #80360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,9 @@ ERROR(invalid_decl_attribute,none,
ERROR(attr_invalid_on_decl_kind,none,
"'%0' attribute cannot be applied to %1 declarations",
(DeclAttribute, DescriptiveDeclKind))
ERROR(attr_invalid_in_context,none,
"'%0' attribute cannot be applied to declaration in %1",
(DeclAttribute, DescriptiveDeclKind))
ERROR(invalid_decl_modifier,none,
"%0 modifier cannot be applied to this declaration", (DeclAttribute))
ERROR(attribute_does_not_apply_to_type,none,
Expand Down
3 changes: 2 additions & 1 deletion include/swift/Frontend/ModuleInterfaceLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,8 @@ struct InterfaceSubContextDelegateImpl : InterfaceSubContextDelegate {
return InterfaceSubContextDelegateImpl::diagnose(interfacePath, diagnosticLoc, SM, Diags, ID, std::move(Args)...);
}
void
inheritOptionsForBuildingInterface(const SearchPathOptions &SearchPathOpts,
inheritOptionsForBuildingInterface(FrontendOptions::ActionType requestedAction,
const SearchPathOptions &SearchPathOpts,
const LangOptions &LangOpts,
const ClangImporterOptions &clangImporterOpts,
const CASOptions &casOpts,
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1332,8 +1332,8 @@ void PrintAST::printAttributes(const Decl *D) {

if (!Options.PrintForSIL) {
// Don't print @_hasStorage if the value is simply stored, or the
// decl is resilient.
if (vd->isResilient() ||
// decl is resilient or in an `@objc @implementation` extension.
if (vd->isResilient() || isInObjCImpl(vd) ||
(vd->getImplInfo().isSimpleStored() &&
!hasLessAccessibleSetter(vd)))
Options.ExcludeAttrList.push_back(DeclAttrKind::HasStorage);
Expand Down
14 changes: 9 additions & 5 deletions lib/Frontend/ModuleInterfaceLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,7 @@ void ModuleInterfaceLoader::collectVisibleTopLevelModuleNames(
}

void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface(
FrontendOptions::ActionType requestedAction,
const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts,
const ClangImporterOptions &clangImporterOpts, const CASOptions &casOpts,
bool suppressRemarks, RequireOSSAModules_t RequireOSSAModules) {
Expand Down Expand Up @@ -1741,9 +1742,12 @@ void InterfaceSubContextDelegateImpl::inheritOptionsForBuildingInterface(
}

// Inhibit warnings from the genericSubInvocation since we are assuming the user
// is not in a position to address them.
genericSubInvocation.getDiagnosticOptions().SuppressWarnings = true;
GenericArgs.push_back("-suppress-warnings");
// is not in a position to address them. (Unless we're verifying, in which
// case they might.)
if (requestedAction != FrontendOptions::ActionType::Typecheck) {
genericSubInvocation.getDiagnosticOptions().SuppressWarnings = true;
GenericArgs.push_back("-suppress-warnings");
}
// Inherit the parent invocation's setting on whether to suppress remarks
if (suppressRemarks) {
genericSubInvocation.getDiagnosticOptions().SuppressRemarks = true;
Expand Down Expand Up @@ -1871,8 +1875,8 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
RequireOSSAModules_t requireOSSAModules)
: SM(SM), Diags(Diags), ArgSaver(Allocator) {
genericSubInvocation.setMainExecutablePath(LoaderOpts.mainExecutablePath);
inheritOptionsForBuildingInterface(searchPathOpts, langOpts,
clangImporterOpts, casOpts,
inheritOptionsForBuildingInterface(LoaderOpts.requestedAction, searchPathOpts,
langOpts, clangImporterOpts, casOpts,
Diags->getSuppressRemarks(),
requireOSSAModules);
// Configure front-end input.
Expand Down
24 changes: 19 additions & 5 deletions lib/Sema/TypeCheckStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3748,8 +3748,12 @@ bool HasStorageRequest::evaluate(Evaluator &evaluator,
return false;

// @_hasStorage implies that it... has storage.
if (var->getAttrs().hasAttribute<HasStorageAttr>())
return true;
if (var->getAttrs().hasAttribute<HasStorageAttr>()) {
// Except in contexts where it would be invalid. This is diagnosed in
// StorageImplInfoRequest.
return !isa<ProtocolDecl, ExtensionDecl, EnumDecl>(
storage->getDeclContext()->getImplementedObjCContext());
}

// Protocol requirements never have storage.
if (isa<ProtocolDecl>(storage->getDeclContext()))
Expand Down Expand Up @@ -3843,10 +3847,21 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator,
: StorageIsMutable);
}

if (auto *var = dyn_cast<VarDecl>(storage)) {
// If we're in an @implementation extension, we care about the semantics of
// the decl it implements.
auto *DC = storage->getDeclContext()->getImplementedObjCContext();

if (auto attr = storage->getParsedAttrs().getAttribute<HasStorageAttr>()) {
// If we see `@_hasStorage` in a context with no stored properties, diagnose
// and ignore it.
if (isa<ExtensionDecl, EnumDecl, ProtocolDecl>(DC)) {
storage->diagnose(diag::attr_invalid_in_context, attr,
DC->getAsDecl()->getDescriptiveKind())
.warnInSwiftInterface(storage->getDeclContext());

// Allow the @_hasStorage attribute to override all the accessors we parsed
// when making the final classification.
if (var->getParsedAttrs().hasAttribute<HasStorageAttr>()) {
} else if (auto *var = dyn_cast<VarDecl>(storage)) {
// The SIL rules for @_hasStorage are slightly different from the non-SIL
// rules. In SIL mode, @_hasStorage marks that the type is simply stored,
// and the only thing that determines mutability is the existence of the
Expand Down Expand Up @@ -3941,7 +3956,6 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator,
bool hasMutableAddress = storage->getParsedAccessor(AccessorKind::MutableAddress);
bool hasInit = storage->getParsedAccessor(AccessorKind::Init);

auto *DC = storage->getDeclContext();
// 'get', 'read', and a non-mutable addressor are all exclusive.
ReadImplKind readImpl;
if (storage->getParsedAccessor(AccessorKind::Get)) {
Expand Down
9 changes: 9 additions & 0 deletions test/ModuleInterface/has_storage_attr_bad.swiftinterface
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// swift-interface-format-version: 1.0
// swift-module-flags:

// RUN: %target-swift-typecheck-module-from-interface(%s) -module-name Test -verify -verify-ignore-unknown
// REQUIRES: OS=macosx

extension Array {
@_hasStorage public var foo: Int { get set } // expected-warning {{'@_hasStorage' attribute cannot be applied to declaration in extension}}
}
8 changes: 8 additions & 0 deletions test/ModuleInterface/objc_implementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import Foundation
// @objc should be omitted on extensions
// NEGATIVE-NOT: @objc{{.*}} extension

// Stored properties in objcImpl extensions shouldn't have @_hasStorage
// NEGATIVE-NOT: @_hasStorage

//
// @_objcImplementation class
//
Expand All @@ -42,6 +45,11 @@ import Foundation
// CHECK-DAG: final public var implProperty2: ObjectiveC.NSObject? { get set }
public final var implProperty2: NSObject?

// CHECK-DAG: final public var implProperty3: ObjectiveC.NSObject? {
public final var implProperty3: NSObject? {
didSet { }
}

// CHECK-NOT: func mainMethod
@objc public func mainMethod(_: Int32) { print(implProperty) }

Expand Down
2 changes: 1 addition & 1 deletion test/SILOptimizer/keypath-folding-crash.sil
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import SwiftShims
import Builtin
import CModule

extension ObjClass {
@objc @implementation extension ObjClass {
@objc @_hasStorage dynamic var o: NSObject { get set }
}

Expand Down
3 changes: 3 additions & 0 deletions test/attr/attributes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ class ExclusivityAttrClass {
class HasStorage {
@_hasStorage var x : Int = 42 // ok, _hasStorage is allowed here
}
extension HasStorage {
@_hasStorage var y : Int { 24 } // expected-error {{'@_hasStorage' attribute cannot be applied to declaration in extension}}
}

@_show_in_interface protocol _underscored {}
@_show_in_interface class _notapplicable {} // expected-error {{may only be used on 'protocol' declarations}}
Expand Down