Skip to content
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: 2 additions & 1 deletion include/swift/AST/DeclContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ struct ConformanceDiagnostic {
ProtocolDecl *ExistingExplicitProtocol;
};

/// Used in diagnostic %selects.
/// Used in diagnostic %selects via FRAGILE_FUNC_KIND.
struct FragileFunctionKind {
enum Kind : unsigned {
Transparent,
Expand All @@ -211,6 +211,7 @@ struct FragileFunctionKind {
DefaultArgument,
PropertyInitializer,
BackDeploy,
EmbeddedAlwaysEmitIntoClient,
None
};

Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7312,7 +7312,8 @@ ERROR(usable_from_inline_attr_in_protocol,none,
"an '@_alwaysEmitIntoClient' function|" \
"a default argument value|" \
"a property initializer in a '@frozen' type|" \
"a '@backDeployed' function'}"
"a '@backDeployed' function|" \
"an embedded function not marked '@_neverEmitIntoClient'}"

ERROR(local_type_in_inlinable_function,
none, "type %0 cannot be nested inside " FRAGILE_FUNC_KIND "1",
Expand Down
29 changes: 28 additions & 1 deletion lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,11 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
case FragileFunctionKind::PropertyInitializer:
case FragileFunctionKind::BackDeploy:
return ResilienceExpansion::Minimal;

/// Embedded functions are treated as fragile for diagnostics only.
/// For code gen they are treated as normal and optimized later.
case FragileFunctionKind::EmbeddedAlwaysEmitIntoClient:

case FragileFunctionKind::None:
return ResilienceExpansion::Maximal;
}
Expand Down Expand Up @@ -551,14 +556,32 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
if (AFD->getDeclContext()->isLocalContext())
continue;

// Delay checking the implicit conditions after explicit declarations.
auto checkEmbeddedAlwaysEmitIntoClient = [&](const ValueDecl *VD) {
if (!VD->getASTContext().LangOpts.hasFeature(Feature::Embedded))
return FragileFunctionKind::None;

bool funcIsNEIC = VD->isNeverEmittedIntoClient();
bool storageIsNEIC = false;
if (auto accessor = dyn_cast<AccessorDecl>(VD))
storageIsNEIC = accessor->getStorage()->isNeverEmittedIntoClient();

// Accessors are implicitly EmbeddedAlwaysEmitIntoClient if neither the
// accessor or starage is marked @_neverEmitIntoClient.
if (!funcIsNEIC && !storageIsNEIC)
return FragileFunctionKind::EmbeddedAlwaysEmitIntoClient;
return FragileFunctionKind::None;
};

auto funcAccess =
AFD->getFormalAccessScope(/*useDC=*/nullptr,
/*treatUsableFromInlineAsPublic=*/true);

// If the function is not externally visible, we will not be serializing
// its body.
if (!funcAccess.isPublic()) {
return {FragileFunctionKind::None};
// For non-public decls, only check embedded mode correctness.
return {checkEmbeddedAlwaysEmitIntoClient(AFD)};
}

// If the function is public, @_transparent implies @inlinable.
Expand Down Expand Up @@ -592,6 +615,10 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
return {FragileFunctionKind::BackDeploy};
}
}

auto implicitKind = checkEmbeddedAlwaysEmitIntoClient(AFD);
if (implicitKind != FragileFunctionKind::None)
return {implicitKind};
}
}

Expand Down
3 changes: 3 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,9 @@ void swift::simple_display(llvm::raw_ostream &out,
case FragileFunctionKind::BackDeploy:
out << "backDeploy";
return;
case FragileFunctionKind::EmbeddedAlwaysEmitIntoClient:
out << "embeddedAlwaysEmitIntoClient";
return;
case FragileFunctionKind::None:
out << "none";
return;
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5223,7 +5223,8 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
// restriction, macros would need to either be expanded when printed in
// swiftinterfaces or be parsable as macros by module clients.
auto fragileKind = DC->getFragileFunctionKind();
if (fragileKind.kind != FragileFunctionKind::None) {
if (fragileKind.kind != FragileFunctionKind::None &&
fragileKind.kind != FragileFunctionKind::EmbeddedAlwaysEmitIntoClient) {
for (auto availSpec : info->getQueries()) {
if (availSpec->getMacroLoc().isValid()) {
diags.diagnose(availSpec->getMacroLoc(),
Expand Down
9 changes: 8 additions & 1 deletion lib/Sema/ResilienceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
return false;
}

// Embedded functions can reference non-public decls as they are visible
// to clients. Still report references to decls imported non-publicly
// to enforce access-level on imports.
ImportAccessLevel problematicImport = D->getImportAccessFrom(DC);
if (fragileKind.kind == FragileFunctionKind::EmbeddedAlwaysEmitIntoClient &&
!problematicImport)
return false;

DowngradeToWarning downgradeToWarning = DowngradeToWarning::No;

// Swift 4.2 did not perform any checks for type aliases.
Expand Down Expand Up @@ -127,7 +135,6 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,

Context.Diags.diagnose(D, diag::resilience_decl_declared_here, D);

ImportAccessLevel problematicImport = D->getImportAccessFrom(DC);
if (problematicImport.has_value() &&
problematicImport->accessLevel < D->getFormalAccess()) {
Context.Diags.diagnose(problematicImport->importLoc,
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2946,7 +2946,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {

// We don't allow nested types inside inlinable contexts.
auto kind = DC->getFragileFunctionKind();
if (kind.kind != FragileFunctionKind::None) {
if (kind.kind != FragileFunctionKind::None &&
kind.kind != FragileFunctionKind::EmbeddedAlwaysEmitIntoClient) {
NTD->diagnose(diag::local_type_in_inlinable_function, NTD->getName(),
kind.getSelector());
}
Expand Down
182 changes: 182 additions & 0 deletions test/Sema/access-level-import-embedded.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/// Test @_implementationOnly internal import exportability diagnostics in embedded mode.

// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \
// RUN: %S/Inputs/implementation-only-imports/indirects.swift \
// RUN: -swift-version 5 -target arm64-apple-none-macho \
// RUN: -enable-experimental-feature Embedded
// RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t \
// RUN: %S/Inputs/implementation-only-imports/directs.swift \
// RUN: -swift-version 5 -target arm64-apple-none-macho \
// RUN: -enable-experimental-feature Embedded

// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unrelated %s -I %t \
// RUN: -swift-version 5 -target arm64-apple-none-macho \
// RUN: -enable-experimental-feature Embedded

// REQUIRES: swift_feature_Embedded
// REQUIRES: embedded_stdlib_cross_compiling

@_implementationOnly internal import directs
// expected-warning @-1 {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}}
// expected-note @-2 19 {{struct 'StructFromDirect' imported as 'internal' from 'directs' here}}
// expected-note @-3 12 {{initializer 'init()' imported as 'internal' from 'directs' here}}
import indirects

internal func localInternalFunc() {} // expected-note {{global function 'localInternalFunc()' is not '@usableFromInline' or public}}

@inlinable
public func explicitlyInlinable(arg: StructFromDirect = StructFromDirect()) {
// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}}
// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}}
// expected-error @-3 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}}
// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}}
// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}}
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}}

if (true) {
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}}
}

func nested() {
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}}
}
nested()

localInternalFunc() // expected-error {{global function 'localInternalFunc()' is internal and cannot be referenced from an '@inlinable' function}}

explicitlyInlinable()
implicitlyInlinablePublic()
implicitlyInlinablePrivate() // expected-error {{global function 'implicitlyInlinablePrivate(arg:)' is private and cannot be referenced from an '@inlinable' function}}
explicitNonInliable()
}

public func implicitlyInlinablePublic(arg: StructFromDirect = StructFromDirect()) {
// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}}
// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}}
// expected-error @-3 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}}
// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}}
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}

if (true) {
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
}

func nested() {
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
}
nested()

localInternalFunc()

explicitlyInlinable()
implicitlyInlinablePublic()
implicitlyInlinablePrivate()
explicitNonInliable()
}

private func implicitlyInlinablePrivate(arg: StructFromDirect = StructFromDirect()) {
// expected-error @-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
// expected-note @-2 {{global function 'implicitlyInlinablePrivate(arg:)' is not '@usableFromInline' or public}}
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}

if (true) {
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
}

func nested() {
_ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
// expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}}
}
nested()

localInternalFunc()

explicitlyInlinable()
implicitlyInlinablePublic()
implicitlyInlinablePrivate()
explicitNonInliable()
}

@_neverEmitIntoClient
public func explicitNonInliable(arg: StructFromDirect = StructFromDirect()) {
// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}}
// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}}
// expected-error @-3 {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}}
// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}}
// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}}
_ = StructFromDirect()

if (true) {
_ = StructFromDirect()
}

@_neverEmitIntoClient
func nested() {
_ = StructFromDirect()
}
nested()

localInternalFunc()

explicitlyInlinable()
implicitlyInlinablePublic()
implicitlyInlinablePrivate()
explicitNonInliable()
}

@_neverEmitIntoClient
internal func explicitNonInliableInternal(arg: StructFromDirect = StructFromDirect()) {
_ = StructFromDirect()

if (true) {
_ = StructFromDirect()
}

@_neverEmitIntoClient
func nested() {
_ = StructFromDirect()
}
nested()

localInternalFunc()

explicitlyInlinable()
implicitlyInlinablePublic()
implicitlyInlinablePrivate()
explicitNonInliable()
}

public func legalAccessToIndirect(arg: StructFromIndirect = StructFromIndirect()) {
_ = StructFromIndirect()

if (true) {
_ = StructFromIndirect()
}

func nested() {
_ = StructFromIndirect()
}
nested()
}

public struct ExposedLayoutPublic {
public var publicField: StructFromDirect // expected-error {{property cannot be declared public because its type uses an internal type}}
// expected-error @-1 {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}}
// expected-note @-2 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}}

private var privateField: StructFromDirect
}

private struct ExposedLayoutPrivate {
private var privateField: StructFromDirect
}
Loading