Skip to content
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

SE-0193: @inlinable implies @usableFromInline #15787

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
20 changes: 16 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ Swift 5.0
Swift 4.2
---------

* [SE-0193][]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for remembering the change log!


Various function-like declarations can now be marked as `@inlinable`,
making their bodies available for optimizations from other modules.

Inlinable function bodies must only reference public declarations, unless
the referenced declaration is marked as `@usableFromInline`.

Note that the presence of the attribute itself does not force inlining or
any other optimization to be performed, nor does it have any effect on
optimizations performed within a single module.

* The C `long double` type is now imported as `Float80` on i386 and x86_64
macOS and Linux. The tgmath functions in the Darwin and glibc modules now
 support `Float80` as well as `Float` and `Double`. Several tgmath
Expand Down Expand Up @@ -103,10 +115,10 @@ Swift 4.2

* [SE-0143][]

Runtime query of conditional conformances is now implemented. Therefore,
a dynamic cast such as `value as? P`, where the dynamic type of `value`
conditionally conforms to `P`, will succeed when the conditional
requirements are met.
Runtime query of conditional conformances is now implemented. Therefore,
a dynamic cast such as `value as? P`, where the dynamic type of `value`
conditionally conforms to `P`, will succeed when the conditional
requirements are met.

**Add new entries to the top of this section, not here!**

Expand Down
9 changes: 6 additions & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3736,15 +3736,18 @@ ERROR(fixed_layout_attr_on_internal_type,
"%select{private|fileprivate|internal|%error|%error}1",
(DeclName, AccessLevel))

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

ERROR(versioned_attr_in_protocol,none,
WARNING(inlinable_implies_usable_from_inline,none,
"'@inlinable' declaration is already '@usableFromInline'",())

ERROR(usable_from_inline_attr_in_protocol,none,
"'@usableFromInline' attribute cannot be used in protocols", ())

ERROR(versioned_dynamic_not_supported,none,
ERROR(usable_from_inline_dynamic_not_supported,none,
"'@usableFromInline' attribute cannot be applied to 'dynamic' declarations", ())

#define FRAGILE_FUNC_KIND \
Expand Down
14 changes: 10 additions & 4 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2106,12 +2106,16 @@ SourceLoc ValueDecl::getAttributeInsertionLoc(bool forModifier) const {
bool ValueDecl::isUsableFromInline() const {
assert(getFormalAccess() == AccessLevel::Internal);

if (getAttrs().hasAttribute<UsableFromInlineAttr>())
if (getAttrs().hasAttribute<UsableFromInlineAttr>() ||
getAttrs().hasAttribute<InlinableAttr>())
return true;

if (auto *accessor = dyn_cast<AccessorDecl>(this))
if (accessor->getStorage()->getAttrs().hasAttribute<UsableFromInlineAttr>())
if (auto *accessor = dyn_cast<AccessorDecl>(this)) {
auto *storage = accessor->getStorage();
if (storage->getAttrs().hasAttribute<UsableFromInlineAttr>() ||
storage->getAttrs().hasAttribute<InlinableAttr>())
return true;
}

if (auto *EED = dyn_cast<EnumElementDecl>(this))
if (EED->getParentEnum()->getAttrs().hasAttribute<UsableFromInlineAttr>())
Expand Down Expand Up @@ -2261,7 +2265,9 @@ void ValueDecl::copyFormalAccessFrom(ValueDecl *source) {
}

// Inherit the @usableFromInline attribute.
if (source->getAttrs().hasAttribute<UsableFromInlineAttr>()) {
if (source->getAttrs().hasAttribute<UsableFromInlineAttr>() &&
!getAttrs().hasAttribute<UsableFromInlineAttr>() &&
!getAttrs().hasAttribute<InlinableAttr>()) {
auto &ctx = getASTContext();
auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true);
getAttrs().add(clonedAttr);
Expand Down
121 changes: 66 additions & 55 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1997,6 +1997,69 @@ static void createStubBody(TypeChecker &tc, ConstructorDecl *ctor) {
ctor->setStubImplementation(true);
}

static void configureDesignatedInitAttributes(TypeChecker &tc,
ClassDecl *classDecl,
ConstructorDecl *ctor,
ConstructorDecl *superclassCtor) {
auto &ctx = tc.Context;

AccessLevel access = classDecl->getFormalAccess();
access = std::max(access, AccessLevel::Internal);
access = std::min(access, superclassCtor->getFormalAccess());

ctor->setAccess(access);

// Inherit the @inlinable attribute.
if (superclassCtor->getFormalAccess(/*useDC=*/nullptr,
/*treatUsableFromInlineAsPublic=*/true)
>= AccessLevel::Public) {
if (superclassCtor->getAttrs().hasAttribute<InlinableAttr>()) {
auto *clonedAttr = new (ctx) InlinableAttr(/*implicit=*/true);
ctor->getAttrs().add(clonedAttr);
}
}

// Inherit the @usableFromInline attribute. We need better abstractions
// for dealing with @usableFromInline.
if (superclassCtor->getFormalAccess(/*useDC=*/nullptr,
/*treatUsableFromInlineAsPublic=*/true)
>= AccessLevel::Public) {
if (access == AccessLevel::Internal &&
!superclassCtor->isDynamic() &&
!ctor->getAttrs().hasAttribute<InlinableAttr>()) {
auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true);
ctor->getAttrs().add(clonedAttr);
}
}

// Make sure the constructor is only as available as its superclass's
// constructor.
AvailabilityInference::applyInferredAvailableAttrs(ctor, superclassCtor, ctx);

if (superclassCtor->isObjC()) {
// Inherit the @objc name from the superclass initializer, if it
// has one.
if (auto objcAttr = superclassCtor->getAttrs().getAttribute<ObjCAttr>()) {
if (objcAttr->hasName()) {
auto *clonedAttr = objcAttr->clone(ctx);
clonedAttr->setImplicit(true);
ctor->getAttrs().add(clonedAttr);
}
}

auto errorConvention = superclassCtor->getForeignErrorConvention();
markAsObjC(tc, ctor, ObjCReason::ImplicitlyObjC, errorConvention);
}
if (superclassCtor->isRequired())
ctor->getAttrs().add(new (ctx) RequiredAttr(/*IsImplicit=*/true));
if (superclassCtor->isDynamic())
ctor->getAttrs().add(new (ctx) DynamicAttr(/*IsImplicit*/true));

// Wire up the overrides.
ctor->getAttrs().add(new (ctx) OverrideAttr(/*IsImplicit=*/true));
ctor->setOverriddenDecl(superclassCtor);
}

ConstructorDecl *
swift::createDesignatedInitOverride(TypeChecker &tc,
ClassDecl *classDecl,
Expand Down Expand Up @@ -2082,66 +2145,14 @@ swift::createDesignatedInitOverride(TypeChecker &tc,

ctor->setImplicit();

AccessLevel access = classDecl->getFormalAccess();
access = std::max(access, AccessLevel::Internal);
access = std::min(access, superclassCtor->getFormalAccess());

ctor->setAccess(access);

// This is really painful. We need better abstractions for dealing with
// @usableFromInline.
if (superclassCtor->getFormalAccess(/*useDC=*/nullptr,
/*treatUsableFromInlineAsPublic=*/true)
>= AccessLevel::Public) {
if (access == AccessLevel::Internal &&
!superclassCtor->isDynamic()) {
auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true);
ctor->getAttrs().add(clonedAttr);
}
}

// Inherit the @inlinable attribute.
if (ctor->getFormalAccess(/*useDC=*/nullptr,
/*treatUsableFromInlineAsPublic=*/true)
>= AccessLevel::Public) {
if (superclassCtor->getAttrs().hasAttribute<InlinableAttr>()) {
auto *clonedAttr = new (ctx) InlinableAttr(/*implicit=*/true);
ctor->getAttrs().add(clonedAttr);
}
}

// Make sure the constructor is only as available as its superclass's
// constructor.
AvailabilityInference::applyInferredAvailableAttrs(ctor, superclassCtor, ctx);

// Set the interface type of the initializer.
ctor->setGenericEnvironment(classDecl->getGenericEnvironmentOfContext());
tc.configureInterfaceType(ctor, ctor->getGenericSignature());

if (superclassCtor->isObjC()) {
// Inherit the @objc name from the superclass initializer, if it
// has one.
if (auto objcAttr = superclassCtor->getAttrs().getAttribute<ObjCAttr>()) {
if (objcAttr->hasName()) {
auto *clonedAttr = objcAttr->clone(ctx);
clonedAttr->setImplicit(true);
ctor->getAttrs().add(clonedAttr);
}
}

auto errorConvention = superclassCtor->getForeignErrorConvention();
markAsObjC(tc, ctor, ObjCReason::ImplicitlyObjC, errorConvention);
}
if (superclassCtor->isRequired())
ctor->getAttrs().add(new (tc.Context) RequiredAttr(/*IsImplicit=*/true));
if (superclassCtor->isDynamic())
ctor->getAttrs().add(new (tc.Context) DynamicAttr(/*IsImplicit*/true));

// Wire up the overrides.
ctor->getAttrs().add(new (tc.Context) OverrideAttr(/*IsImplicit=*/true));
ctor->setOverriddenDecl(superclassCtor);
ctor->setValidationStarted();

configureDesignatedInitAttributes(tc, classDecl,
ctor, superclassCtor);

if (kind == DesignatedInitKind::Stub) {
// Make this a stub implementation.
createStubBody(tc, ctor);
Expand Down
21 changes: 13 additions & 8 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1959,13 +1959,14 @@ void AttributeChecker::visitUsableFromInlineAttr(UsableFromInlineAttr *attr) {
// FIXME: Once protocols can contain nominal types, do we want to allow
// these nominal types to have access control (and also @usableFromInline)?
if (isa<ProtocolDecl>(VD->getDeclContext())) {
diagnoseAndRemoveAttr(attr, diag::versioned_attr_in_protocol);
diagnoseAndRemoveAttr(attr, diag::usable_from_inline_attr_in_protocol);
return;
}

// @usableFromInline can only be applied to internal declarations.
if (VD->getFormalAccess() != AccessLevel::Internal) {
diagnoseAndRemoveAttr(attr, diag::versioned_attr_with_explicit_access,
diagnoseAndRemoveAttr(attr,
diag::usable_from_inline_attr_with_explicit_access,
VD->getFullName(),
VD->getFormalAccess());
return;
Expand All @@ -1974,7 +1975,13 @@ void AttributeChecker::visitUsableFromInlineAttr(UsableFromInlineAttr *attr) {
// Symbols of dynamically-dispatched declarations are never referenced
// directly, so marking them as @usableFromInline does not make sense.
if (VD->isDynamic()) {
diagnoseAndRemoveAttr(attr, diag::versioned_dynamic_not_supported);
diagnoseAndRemoveAttr(attr, diag::usable_from_inline_dynamic_not_supported);
return;
}

// On internal declarations, @inlinable implies @usableFromInline.
if (VD->getAttrs().hasAttribute<InlinableAttr>()) {
diagnoseAndRemoveAttr(attr, diag::inlinable_implies_usable_from_inline);
return;
}
}
Expand Down Expand Up @@ -2003,11 +2010,9 @@ void AttributeChecker::visitInlinableAttr(InlinableAttr *attr) {
return;
}

// @inlinable can only be applied to public or @usableFromInline
// declarations.
auto access = VD->getFormalAccess(/*useDC=*/nullptr,
/*treatUsableFromInlineAsPublic=*/true);
if (access < AccessLevel::Public) {
// @inlinable can only be applied to public or internal declarations.
auto access = VD->getFormalAccess();
if (access < AccessLevel::Internal) {
diagnoseAndRemoveAttr(attr, diag::inlinable_decl_not_public,
VD->getBaseName(),
access);
Expand Down
2 changes: 0 additions & 2 deletions stdlib/public/core/Algorithm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ public struct EnumeratedIterator<Base: IteratorProtocol> {

/// Construct from a `Base` iterator.
@inlinable
@usableFromInline
internal init(_base: Base) {
self._base = _base
self._count = 0
Expand Down Expand Up @@ -145,7 +144,6 @@ public struct EnumeratedSequence<Base: Sequence> {

/// Construct from a `Base` sequence.
@inlinable
@usableFromInline
internal init(_base: Base) {
self._base = _base
}
Expand Down
11 changes: 0 additions & 11 deletions stdlib/public/core/AnyHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,26 +61,22 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
internal var _baseHashable: Base

@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal init(_ base: Base) {
self._baseHashable = base
}


@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal var _typeID: ObjectIdentifier {
return ObjectIdentifier(type(of: self))
}

@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal func _unbox<T : Hashable>() -> T? {
return (self as _AnyHashableBox as? _ConcreteHashableBox<T>)?._baseHashable
}

@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal func _isEqual(to rhs: _AnyHashableBox) -> Bool? {
if let rhs: Base = rhs._unbox() {
return _baseHashable == rhs
Expand All @@ -89,25 +85,21 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
}

@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal var _hashValue: Int {
return _baseHashable.hashValue
}

@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
func _hash(_into hasher: inout _Hasher) {
_baseHashable._hash(into: &hasher)
}

@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal var _base: Any {
return _baseHashable
}

@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal
func _downCastConditional<T>(into result: UnsafeMutablePointer<T>) -> Bool {
guard let value = _baseHashable as? T else { return false }
Expand All @@ -122,7 +114,6 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
// turns a non-custom representation into a custom one, which is used as
// the lowest-common-denominator for comparisons.
@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal func _getBridgedCustomAnyHashable<T>(_ value: T) -> AnyHashable? {
let bridgedValue = _bridgeAnythingToObjectiveC(value)
return (bridgedValue as?
Expand Down Expand Up @@ -191,7 +182,6 @@ public struct AnyHashable {
}

@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal init<H : Hashable>(_usingDefaultRepresentationOf base: H) {
self._box = _ConcreteHashableBox(base)
self._usedCustomRepresentation = false
Expand All @@ -217,7 +207,6 @@ public struct AnyHashable {
/// This avoids the intermediate re-boxing we would get if we just did
/// a downcast on `base`.
@inlinable // FIXME(sil-serialize-all)
@usableFromInline // FIXME(sil-serialize-all)
internal
func _downCastConditional<T>(into result: UnsafeMutablePointer<T>) -> Bool {
// Attempt the downcast.
Expand Down
Loading