Skip to content

[AST] NFC: Enable reference storage type meta-programming #16237

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 13 commits into from
Jul 5, 2018
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
5 changes: 2 additions & 3 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ TYPE_ATTR(escaping)
TYPE_ATTR(block_storage)
TYPE_ATTR(box)
TYPE_ATTR(dynamic_self)
TYPE_ATTR(sil_unowned)
TYPE_ATTR(sil_unmanaged)
TYPE_ATTR(sil_weak)
#define REF_STORAGE(Name, name, ...) TYPE_ATTR(sil_##name)
#include "swift/AST/ReferenceStorage.def"
TYPE_ATTR(error)
TYPE_ATTR(out)
TYPE_ATTR(in)
Expand Down
15 changes: 6 additions & 9 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,16 @@ class TypeAttributes {
return getOwnership() != ReferenceOwnership::Strong;
}
ReferenceOwnership getOwnership() const {
if (has(TAK_sil_weak))
return ReferenceOwnership::Weak;
if (has(TAK_sil_unowned))
return ReferenceOwnership::Unowned;
if (has(TAK_sil_unmanaged))
return ReferenceOwnership::Unmanaged;
#define REF_STORAGE(Name, name, ...) \
if (has(TAK_sil_##name)) return ReferenceOwnership::Name;
#include "swift/AST/ReferenceStorage.def"
return ReferenceOwnership::Strong;
}

void clearOwnership() {
clearAttribute(TAK_sil_weak);
clearAttribute(TAK_sil_unowned);
clearAttribute(TAK_sil_unmanaged);
#define REF_STORAGE(Name, name, ...) \
clearAttribute(TAK_sil_##name);
#include "swift/AST/ReferenceStorage.def"
}

bool hasOpenedID() const { return OpenedID.hasValue(); }
Expand Down
9 changes: 3 additions & 6 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -551,12 +551,9 @@ ERROR(sil_invalid_instr_operands,none,
"invalid instruction operands", ())
ERROR(sil_operand_not_address,none,
"%0 operand of '%1' must have address type", (StringRef, StringRef))
ERROR(sil_operand_not_unowned_address,none,
"%0 operand of '%1' must have address of [unowned] type",
(StringRef, StringRef))
ERROR(sil_operand_not_weak_address,none,
"%0 operand of '%1' must have address of [weak] type",
(StringRef, StringRef))
ERROR(sil_operand_not_ref_storage_address,none,
"%0 operand of '%1' must have address of %2 type",
(StringRef, StringRef, ReferenceOwnership))
ERROR(sil_integer_literal_not_integer_type,none,
"integer_literal instruction requires a 'Builtin.Int<n>' type", ())
ERROR(sil_float_literal_not_float_type,none,
Expand Down
10 changes: 6 additions & 4 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3392,10 +3392,12 @@ ERROR(invalid_ownership_protocol_type,none,
(ReferenceOwnership, Type))
ERROR(invalid_ownership_with_optional,none,
"%0 variable cannot have optional type", (ReferenceOwnership))
ERROR(invalid_weak_ownership_not_optional,none,
"'weak' variable should have optional type %0", (Type))
ERROR(invalid_weak_let,none,
"'weak' must be a mutable variable, because it may change at runtime", ())
ERROR(invalid_ownership_not_optional,none,
"%0 variable should have optional type %1",
(ReferenceOwnership, Type))
ERROR(invalid_ownership_is_let,none,
"%0 must be a mutable variable, because it may change at runtime",
(ReferenceOwnership))
ERROR(ownership_invalid_in_protocols,none,
"%0 cannot be applied to a property declaration in a protocol",
(ReferenceOwnership))
Expand Down
91 changes: 81 additions & 10 deletions include/swift/AST/Ownership.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#define SWIFT_OWNERSHIP_H

#include "swift/Basic/InlineBitfield.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <stdint.h>

Expand All @@ -32,20 +34,85 @@ enum class ReferenceOwnership : uint8_t {
/// \brief a strong reference (the default semantics)
Strong,

/// \brief a 'weak' reference
Weak,
#define REF_STORAGE(Name, ...) Name,
#define REF_STORAGE_RANGE(First, Last) Last_Kind = Last
#include "swift/AST/ReferenceStorage.def"
};

enum : unsigned { NumReferenceOwnershipBits =
countBitsUsed(static_cast<unsigned>(ReferenceOwnership::Last_Kind)) };

static inline llvm::StringRef keywordOf(ReferenceOwnership ownership) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not super happy with dumping all of these in the swift:: namespace. Maybe there should be a sub-namespace?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This seems separable and larger than the pull request at hand. As the person to "blame" for many of these Num*Bits enums, I'd be happy to discuss in the forums what the "right" tradeoff is and then implement it. Do you want to start a thread?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not the Num*Bits I care about, it's keywordOf and Optionality and things like that. I know the type signature shows what they're for, but this is still a header that gets included all over the place, and it's weird to put these generically-named declarations in the common namespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldn't find any convention in Swift for how to workaround the fact that C++ doesn't allow methods on enum types. I'm open to suggestions.

In the case of this patch series and the way it is evolving, I could probably just move this logic to methods on ReferenceStorageType.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we've just begrudgingly accepted the need to have fairly general-looking function names in the Swift namespace.

Copy link
Contributor

Choose a reason for hiding this comment

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

Optionality in particular is really bad, compared to OptionalTypeKind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not in love with the name either. I just want to abstract away the hardcoded knowledge about the "optionality" of the reference storage types. How about OwnershipOptionality? Or ReferenceStorageOptionality?

Copy link
Contributor

Choose a reason for hiding this comment

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

Either of those is better, yeah. I think I'd lean towards the latter—this is not something that needs to be particularly concise.

Copy link
Contributor Author

@davezarzycki davezarzycki May 4, 2018

Choose a reason for hiding this comment

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

After some thought, I'm going with ReferenceOwnershipOptionality over ReferenceStorageOptionality. The rational is threefold:

  1. The default ReferenceOwnership is not a reference storage type.
  2. An enum over just the reference storage types does not exist and I feel that there is not sufficient demand for such an enum to exist.
  3. I don't want to create a situation where the callers of optionalityOf() need to check for Strong first before calling the API.

switch (ownership) {
case ReferenceOwnership::Strong:
assert(false && "not applicable");
case ReferenceOwnership::Weak: return "weak";
case ReferenceOwnership::Unowned: return "unowned";
case ReferenceOwnership::Unmanaged: return "unowned(unsafe)";
}
assert(false && "impossible");
}

/// \brief an 'unowned' reference
Unowned,
static inline llvm::StringRef manglingOf(ReferenceOwnership ownership) {
switch (ownership) {
case ReferenceOwnership::Strong:
assert(false && "not applicable");
case ReferenceOwnership::Weak: return "Xw";
case ReferenceOwnership::Unowned: return "Xo";
case ReferenceOwnership::Unmanaged: return "Xu";
}
assert(false && "impossible");
}

/// \brief an 'unowned(unsafe)' reference
Unmanaged,
static inline bool isLessStrongThan(ReferenceOwnership left,
ReferenceOwnership right) {
auto strengthOf = [] (ReferenceOwnership ownership) -> int {
// A reference can be optimized away if outlived by a stronger reference.
// NOTES:
// 1) Different reference kinds of the same strength are NOT interchangable.
// 2) Stronger than "strong" might include locking, for example.
// 3) Unchecked references must be last to preserve identity comparisons
// until the last checked reference is dead.
// 4) Please keep the switch statement ordered to ease code review.
switch (ownership) {
case ReferenceOwnership::Strong: return 0;
case ReferenceOwnership::Unowned: return -1;
case ReferenceOwnership::Weak: return -1;
#define UNCHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: return INT_MIN;
#include "swift/AST/ReferenceStorage.def"
}
llvm_unreachable("impossible");
};

Last_Kind = Unmanaged
return strengthOf(left) < strengthOf(right);
}

enum class ReferenceOwnershipOptionality : uint8_t {
Disallowed,
Allowed,
AllowedIfImporting,
Required,

Last_Kind = Required
};
enum : unsigned { NumOptionalityBits = countBitsUsed(static_cast<unsigned>(
ReferenceOwnershipOptionality::Last_Kind)) };

enum : unsigned { NumReferenceOwnershipBits =
countBitsUsed(static_cast<unsigned>(ReferenceOwnership::Last_Kind)) };
static inline ReferenceOwnershipOptionality
optionalityOf(ReferenceOwnership ownership) {
switch (ownership) {
case ReferenceOwnership::Strong:
return ReferenceOwnershipOptionality::Allowed;
case ReferenceOwnership::Weak:
return ReferenceOwnershipOptionality::Required;
case ReferenceOwnership::Unowned:
return ReferenceOwnershipOptionality::Disallowed;
case ReferenceOwnership::Unmanaged:
return ReferenceOwnershipOptionality::AllowedIfImporting;
}
llvm_unreachable("impossible");
}

/// Diagnostic printing of \c StaticSpellingKind.
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceOwnership RO);
Expand All @@ -60,8 +127,12 @@ enum class ValueOwnership : uint8_t {
/// \brief a '__shared' non-mutating pointer-like value
Shared,
/// \brief an '__owned' value
Owned
Owned,

Last_Kind = Owned
};
enum : unsigned { NumValueOwnershipBits =
countBitsUsed(static_cast<unsigned>(ValueOwnership::Last_Kind)) };

} // end namespace swift

Expand Down
197 changes: 197 additions & 0 deletions include/swift/AST/ReferenceStorage.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
//===--- ReferenceStorage.def - Non-default reference storage ---*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines non-default reference storage kind macros used for
// macro-metaprogramming.
//
//===----------------------------------------------------------------------===//

/// There are two fundamental reference storage types: checked and unchecked.
/// Checked storage types have runtime enforced correctness.
/// Unchecked storage types have no runtime enforced correctness.
///
/// Checked reference storage types are also subcategorized by loadability.
/// * Always loadable: The compiler may move the reference or use registers.
/// * Never loadable: The runtime (etc) tracks the address of the reference.
/// * Sometimes loadable: If the reference is a native object, then it is
/// always loadable. Otherwise fall back to never loadable semantics, a.k.a.
/// "address only".
///
/// Unchecked reference storage types are always loadable.
///
/// The primary macros therefore are:
/// * ALWAYS_LOADABLE_CHECKED_REF_STORAGE
/// * SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
/// * NEVER_LOADABLE_CHECKED_REF_STORAGE
/// * UNCHECKED_REF_STORAGE
///
/// Helper macros include:
/// * CHECKED_REF_STORAGE -- Any checked reference storage type. Specifically
/// "always", "sometimes", and "never" -- but not "unchecked".
/// * LOADABLE_REF_STORAGE -- Any loadable reference storage type. Specifically
/// "always", "sometimes", and "unchecked" -- but not "never".
/// * ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE -- self describing.
/// * NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE -- self describing.
///
/// SUBSYSTEMS NOTES
///
/// In general, reference storage types are barely visible in the user facing
/// type system and therefore AST clients above SIL can get away with
/// just REF_STORAGE, or CHECKED_REF_STORAGE with UNCHECKED_REF_STORAGE.
///
/// When it comes to SIL aware AST clients, loadability matters. The best way
/// to understand how the helper macros are used is to look at SILNodes.def.
/// What follows is a short -- possibly not up to date -- summary:
///
/// UNCHECKED_REF_STORAGE
/// Name##RetainValueInst
/// Name##ReleaseValueInst
/// LOADABLE_REF_STORAGE
/// Ref*ToNameInst
/// Name*ToRefInst
/// NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
/// Load##Name##Inst
/// Store##Name##Inst
/// ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
/// Copy##Name##ValueInst
/// StrongRetain##Name##Inst
/// Name##RetainInst
/// Name##ReleaseInst
///
/// After helper macro expansion:
///
/// UNCHECKED_REF_STORAGE
/// Ref*ToNameInst
/// Name*ToRefInst
/// Name##RetainValueInst
/// Name##ReleaseValueInst
/// ALWAYS_LOADABLE_CHECKED_REF_STORAGE
/// Ref*ToNameInst
/// Name*ToRefInst
/// Copy##Name##ValueInst
/// StrongRetain##Name##Inst
/// Name##RetainInst
/// Name##ReleaseInst
/// SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
/// Ref*ToNameInst
/// Name*ToRefInst
/// Load##Name##Inst
/// Store##Name##Inst
/// Copy##Name##ValueInst
/// StrongRetain##Name##Inst
/// Name##RetainInst
/// Name##ReleaseInst
/// NEVER_LOADABLE_CHECKED_REF_STORAGE
/// Load##Name##Inst
/// Store##Name##Inst
///
/// Finally, a note about IRGen: TypeInfos need to be created per reference
/// storage type, and SOMETIMES_LOADABLE_CHECKED_REF_STORAGE needs *two*
/// TypeInfos to be created. One for the loadable scenario, and one for the
/// address-only scenario.


#ifndef REF_STORAGE
#define REF_STORAGE(Name, name, NAME)
#endif

#ifdef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
#if defined(ALWAYS_LOADABLE_CHECKED_REF_STORAGE) || \
defined(SOMETIMES_LOADABLE_CHECKED_REF_STORAGE)
#error Overlapping meta-programming macros
#endif
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME)
#endif

#ifdef NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
#if defined(NEVER_LOADABLE_CHECKED_REF_STORAGE) || \
defined(SOMETIMES_LOADABLE_CHECKED_REF_STORAGE)
#error Overlapping meta-programming macros
#endif
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME)
#endif

#ifdef LOADABLE_REF_STORAGE
#if defined(ALWAYS_LOADABLE_CHECKED_REF_STORAGE) || \
defined(SOMETIMES_LOADABLE_CHECKED_REF_STORAGE) || \
defined(UNCHECKED_REF_STORAGE)
#error Overlapping meta-programming macros
#endif
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
LOADABLE_REF_STORAGE(Name, name, NAME)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
LOADABLE_REF_STORAGE(Name, name, NAME)
#define UNCHECKED_REF_STORAGE(Name, name, NAME) \
LOADABLE_REF_STORAGE(Name, name, NAME)
#endif

#ifdef CHECKED_REF_STORAGE
#if defined(SOMETIMES_LOADABLE_CHECKED_REF_STORAGE) || \
defined(ALWAYS_LOADABLE_CHECKED_REF_STORAGE) || \
defined(NEVER_LOADABLE_CHECKED_REF_STORAGE)
#error Overlapping meta-programming macros
#endif
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
CHECKED_REF_STORAGE(Name, name, NAME)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
CHECKED_REF_STORAGE(Name, name, NAME)
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
CHECKED_REF_STORAGE(Name, name, NAME)
#endif

#ifndef NEVER_LOADABLE_CHECKED_REF_STORAGE
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
REF_STORAGE(Name, name, NAME)
#endif

#ifndef SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
REF_STORAGE(Name, name, NAME)
#endif

#ifndef ALWAYS_LOADABLE_CHECKED_REF_STORAGE
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, name, NAME) \
REF_STORAGE(Name, name, NAME)
#endif

#ifndef UNCHECKED_REF_STORAGE
#define UNCHECKED_REF_STORAGE(Name, name, NAME) \
REF_STORAGE(Name, name, NAME)
#endif

#ifndef REF_STORAGE_RANGE
#define REF_STORAGE_RANGE(First, Last)
#endif

// NOTE: You will need to update ReferenceOwnership in ModuleFormat.h.
NEVER_LOADABLE_CHECKED_REF_STORAGE(Weak, weak, WEAK)
SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Unowned, unowned, UNOWNED)
UNCHECKED_REF_STORAGE(Unmanaged, unmanaged, UNMANAGED)
REF_STORAGE_RANGE(Weak, Unmanaged)

#undef REF_STORAGE
#undef NEVER_LOADABLE_CHECKED_REF_STORAGE
#undef ALWAYS_LOADABLE_CHECKED_REF_STORAGE
#undef SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
#undef UNCHECKED_REF_STORAGE
#undef REF_STORAGE_RANGE

#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
#undef NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
#undef LOADABLE_REF_STORAGE
#undef CHECKED_REF_STORAGE
Loading