Skip to content

[AST/Sema] Make it possible to use init accessors in inlinable initializers #75191

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 4 commits into from
Jul 15, 2024
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
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6904,8 +6904,8 @@ ERROR(frozen_attr_on_internal_type,

ERROR(usable_from_inline_attr_with_explicit_access,
none, "'@usableFromInline' attribute can only be applied to internal or package "
"declarations, but %0 is %select{private|fileprivate|%error|package|public|open}1",
(DeclName, AccessLevel))
"declarations, but %kind0 is %select{private|fileprivate|%error|package|public|open}1",
(const ValueDecl *, AccessLevel))

WARNING(inlinable_implies_usable_from_inline,none,
"'@usableFromInline' attribute has no effect on '@inlinable' %kind0",
Expand Down
5 changes: 0 additions & 5 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,6 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
}
}

if (auto *accessor = dyn_cast<AccessorDecl>(D)) {
if (accessor->isInitAccessor() && !options.PrintForSIL)
return false;
}

return ShouldPrintChecker::shouldPrint(D, options);
}
};
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/AccessRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const {
return AccessLevel::Private;
case AccessorKind::Init:
// These are only called from within the same module.
return std::min(storage->getFormalAccess(), AccessLevel::Internal);
return AccessLevel::Internal;
}
}

Expand Down
13 changes: 12 additions & 1 deletion lib/Sema/ResilienceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
auto *DC = where.getDeclContext();
auto &Context = DC->getASTContext();

if (auto *init = dyn_cast<ConstructorDecl>(DC)) {
if (init->isDesignatedInit()) {
auto *storage = dyn_cast<AbstractStorageDecl>(D);
if (storage && storage->hasInitAccessor()) {
if (diagnoseInlinableDeclRefAccess(
loc, storage->getAccessor(AccessorKind::Init), where))
return true;
}
}
}

ImportAccessLevel problematicImport = D->getImportAccessFrom(DC);
if (problematicImport.has_value()) {
auto SF = DC->getParentSourceFile();
Expand Down Expand Up @@ -112,7 +123,7 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,

// Swift 4.2 did not check accessor accessibility.
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
if (!Context.isSwiftVersionAtLeast(5))
if (!accessor->isInitAccessor() && !Context.isSwiftVersionAtLeast(5))
downgradeToWarning = DowngradeToWarning::Yes;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3273,7 +3273,7 @@ void AttributeChecker::visitUsableFromInlineAttr(UsableFromInlineAttr *attr) {
VD->getFormalAccess() != AccessLevel::Package) {
diagnoseAndRemoveAttr(attr,
diag::usable_from_inline_attr_with_explicit_access,
VD->getName(), VD->getFormalAccess());
VD, VD->getFormalAccess());
return;
}

Expand Down
8 changes: 4 additions & 4 deletions test/Compatibility/attr_usableFromInline_swift4.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// RUN: %target-typecheck-verify-swift -enable-testing -swift-version 4 -disable-objc-attr-requires-foundation-module -enable-objc-interop

@usableFromInline private func privateVersioned() {}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but 'privateVersioned()' is private}}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but global function 'privateVersioned()' is private}}

@usableFromInline fileprivate func fileprivateVersioned() {}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but 'fileprivateVersioned()' is fileprivate}}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but global function 'fileprivateVersioned()' is fileprivate}}

@usableFromInline internal func internalVersioned() {}
// OK
Expand All @@ -14,11 +14,11 @@
// OK

@usableFromInline public func publicVersioned() {}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but 'publicVersioned()' is public}}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but global function 'publicVersioned()' is public}}

internal class InternalClass {
@usableFromInline public func publicVersioned() {}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but 'publicVersioned()' is public}}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but instance method 'publicVersioned()' is public}}
}

fileprivate class filePrivateClass {
Expand Down
8 changes: 4 additions & 4 deletions test/Compatibility/attr_usableFromInline_swift42.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// RUN: %target-typecheck-verify-swift -enable-testing -swift-version 4.2 -disable-objc-attr-requires-foundation-module -enable-objc-interop

@usableFromInline private func privateVersioned() {}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but 'privateVersioned()' is private}}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but global function 'privateVersioned()' is private}}

@usableFromInline fileprivate func fileprivateVersioned() {}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but 'fileprivateVersioned()' is fileprivate}}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but global function 'fileprivateVersioned()' is fileprivate}}

@usableFromInline internal func internalVersioned() {}
// OK
Expand All @@ -14,12 +14,12 @@
// OK

@usableFromInline public func publicVersioned() {}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but 'publicVersioned()' is public}}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but global function 'publicVersioned()' is public}}

internal class InternalClass {
// expected-note@-1 2{{type declared here}}
@usableFromInline public func publicVersioned() {}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but 'publicVersioned()' is public}}
// expected-error@-1 {{'@usableFromInline' attribute can only be applied to internal or package declarations, but instance method 'publicVersioned()' is public}}
}

fileprivate class filePrivateClass {
Expand Down
79 changes: 79 additions & 0 deletions test/Interpreter/inlinable_init_accessors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// RUN: %empty-directory(%t/src)
// RUN: split-file %s %t/src

// RUN: %target-build-swift %t/src/Library.swift -swift-version 5 -emit-module -emit-library \
// RUN: -enable-library-evolution \
// RUN: -module-name Library \
// RUN: -o %t/%target-library-name(Library) \
// RUN: -emit-module-interface-path %t/Library.swiftinterface

// RUN: %target-codesign %t/%target-library-name(Library)

// RUN: %target-build-swift -I %t -L %t -l Library %t/src/main.swift %target-rpath(%t) -o %t/main.out
// RUN: %target-codesign %t/main.out
// RUN: %target-run %t/main.out %t/%target-library-name(Library) 2>&1 | %FileCheck %t/src/main.swift

// RUN: rm %t/Library.swiftmodule

// RUN: %target-build-swift -I %t -L %t -l Library %t/src/main.swift %target-rpath(%t) -o %t/main.out
// RUN: %target-codesign %t/main.out
// RUN: %target-run %t/main.out %t/%target-library-name(Library) 2>&1 | %FileCheck %t/src/main.swift

// REQUIRES: executable_test

//--- Library.swift
@frozen
public struct Inlinable {
var _x: Int

public var x: Int {
@usableFromInline
@storageRestrictions(initializes: _x)
init {
self._x = newValue
}

get {
_x
}
}

@inlinable
public init(x: Int) {
self.x = x
}
}

@frozen
public struct Transparent {
@usableFromInline
var _x: Int

public var x: Int {
@_alwaysEmitIntoClient
@storageRestrictions(initializes: _x)
init {
self._x = newValue
}

get {
_x
}
}

@_alwaysEmitIntoClient
public init(x: Int) {
self.x = x
}
}

//--- main.swift
import Library

let inlinable = Inlinable(x: 42)
print("Inlinable.x = \(inlinable.x)")
// CHECK: Inlinable.x = 42

let transparent = Transparent(x: -1)
print("Transparent.x = \(transparent.x)")
// CHECK: Transparent.x = -1
123 changes: 123 additions & 0 deletions test/ModuleInterface/init_accessors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// RUN: %empty-directory(%t/src)
// RUN: split-file %s %t/src

/// Build the library A
// RUN: %target-swift-frontend -emit-module %t/src/A.swift \
// RUN: -module-name A -swift-version 5 -enable-library-evolution \
// RUN: -emit-module-path %t/A.swiftmodule \
// RUN: -emit-module-interface-path %t/A.swiftinterface

// RUN: %FileCheck %t/src/A.swift < %t/A.swiftinterface

// Build the client using module
// RUN: %target-swift-emit-sil -verify -module-name Client -I %t %t/src/Client.swift | %FileCheck %t/src/Client.swift

// RUN: rm %t/A.swiftmodule

// Re-build the client using interface
// RUN: %target-swift-emit-sil -verify -module-name Client -I %t %t/src/Client.swift | %FileCheck %t/src/Client.swift

// REQUIRES: asserts

//--- A.swift
@frozen
public struct Inlinable {
var _x: Int

// CHECK: public var x: Swift.Int {
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: @storageRestrictions(initializes: _x) init
// CHECK-NEXT: get
// CHECK-NEXT }

public var x: Int {
@usableFromInline
@storageRestrictions(initializes: _x)
init {
self._x = newValue
}

get {
_x
}
}

@inlinable
public init(x: Int) {
self.x = x
}
}

public struct Internal {
// CHECK: public var y: Swift.Int {
// CHECK-NEXT: get
// CHECK-NEXT: }

public var y: Int {
init {
}

get { 0 }
}

init(y: Int) {
self.y = y
}
}

@frozen
public struct Transparent {
@usableFromInline
var _x: Int

// CHECK: public var x: Swift.Int {
// CHECK-NEXT: @_alwaysEmitIntoClient @storageRestrictions(initializes: _x) init {
// CHECK-NEXT: self._x = newValue
// CHECK-NEXT: }
// CHECK-NEXT: get
// CHECK-NEXT }

public var x: Int {
@_alwaysEmitIntoClient
@storageRestrictions(initializes: _x)
init {
self._x = newValue
}

get {
_x
}
}

@_alwaysEmitIntoClient
public init(x: Int) {
self.x = x
}
}

//--- Client.swift
import A

// CHECK-LABEL: sil hidden @$s6Client15testTransparentyyF : $@convention(thin) () -> ()
// CHECK: [[X:%.*]] = struct $Int (%1 : $Builtin.Int64)
// CHECK-NEXT: // function_ref Transparent.init(x:)
// CHECK-NEXT: [[TRANSPARENT_REF:%.*]] = function_ref @$s1A11TransparentV1xACSi_tcfC : $@convention(method) (Int, @thin Transparent.Type) -> Transparent
// CHECK-NEXT: apply [[TRANSPARENT_REF]]([[X]], %0) : $@convention(method) (Int, @thin Transparent.Type) -> Transparent
func testTransparent() {
_ = Transparent(x: 42)
}

// CHECK-LABEL: sil shared @$s1A11TransparentV1xACSi_tcfC : $@convention(method) (Int, @thin Transparent.Type) -> Transparent

// CHECK-LABEL: sil hidden @$s6Client13testInlinableyyF : $@convention(thin) () -> ()
// CHECK: [[X:%.*]] = struct $Int (%1 : $Builtin.Int64)
// CHECK-NEXT: // function_ref Inlinable.init(x:)
// CHECK-NEXT: [[INLINABLE_REF:%.*]] = function_ref @$s1A9InlinableV1xACSi_tcfC : $@convention(method) (Int, @thin Inlinable.Type) -> Inlinable
// CHECK-NEXT: apply [[INLINABLE_REF]]([[X]], %0) : $@convention(method) (Int, @thin Inlinable.Type) -> Inlinable
func testInlinable() {
_ = Inlinable(x: 42)
}

// CHECK-LABEL: sil @$s1A9InlinableV1xACSi_tcfC : $@convention(method) (Int, @thin Inlinable.Type) -> Inlinable

// CHECK-LABEL: sil shared @$s1A11TransparentV1xSivi : $@convention(thin) (Int, @thin Transparent.Type) -> @out Int
29 changes: 28 additions & 1 deletion test/attr/attr_alwaysEmitIntoClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,31 @@ public func publicFunction() {}
internalFunction() // expected-error {{global function 'internalFunction()' is internal and cannot be referenced from an '@_alwaysEmitIntoClient' function}}
versionedFunction()
publicFunction()
}
}

public struct TestInitAccessors {
var _x: Int

public var x: Int {
@storageRestrictions(initializes: _x)
init { // expected-note 2 {{init acecssor for property 'x' is not '@usableFromInline' or public}}
self._x = newValue
}

get {
self._x
}

set {}
}

@_alwaysEmitIntoClient
public init(x: Int) {
self.x = 0 // expected-error {{init acecssor for property 'x' is internal and cannot be referenced from an '@_alwaysEmitIntoClient' function}}
}

@inlinable
public init() {
self.x = 0 // expected-error {{init acecssor for property 'x' is internal and cannot be referenced from an '@inlinable' function}}
}
}
Loading