//===--- 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
///   StrongCopy##Name##ValueInst
/// 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
///   StrongCopy##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
///   StrongCopy##Name##ValueInst
///   StrongRetain##Name##Inst
///   Name##RetainInst
///   Name##ReleaseInst
/// SOMETIMES_LOADABLE_CHECKED_REF_STORAGE
///   Ref*ToNameInst
///   Name*ToRefInst
///   Load##Name##Inst
///   Store##Name##Inst
///   StrongCopy##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.
///
/// TODO: We should change Copy##Name##ValueInst to be defined on
/// LOADABLE_REF_STORAGE. It just will require us to go through, refactor, and
/// fix up this code.


#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
