//===- SwiftAbstractBasicWriter.h - Clang serialization adapter -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 provides an intermediate CRTP class which implements most of
// Clang's AbstractBasicWriter interface, allowing largely the same logic
// to be used for both the importer's "can this be serialized" checks and
// the serializer's actual serialization logic.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICWRITER_H
#define SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICWRITER_H

#include "clang/AST/AbstractTypeWriter.h"

namespace swift {

/// An implementation of Clang's AbstractBasicWriter interface for a Swift
/// datastream-based reader.  This is paired with the AbstractBasicReader
/// implementation in SwiftAbstractBasicReader.h.  Note that the general
/// expectation is that the types and declarations involved will have passed
/// a serializability check when this is used for actual serialization.
/// The code in this class is also used when implementing that
/// serializability check and so must be a little more cautious.
///
/// The subclass must implement:
///   void writeUInt64(uint64_t value);
///   void writeIdentifier(const clang::IdentifierInfo *ident);
///   void writeStmtRef(const clang::Stmt *stmt);
///   void writeDeclRef(const clang::Decl *decl);
template <class Impl>
class DataStreamBasicWriter
       : public clang::serialization::DataStreamBasicWriter<Impl> {
  using super = clang::serialization::DataStreamBasicWriter<Impl>;
public:
  using super::asImpl;

  DataStreamBasicWriter(clang::ASTContext &ctx) : super(ctx) {}

  /// Perform all the calls necessary to write out the given type.
  void writeTypeRef(const clang::Type *type) {
    asImpl().writeUInt64(uint64_t(type->getTypeClass()));
    clang::serialization::AbstractTypeWriter<Impl>(asImpl()).write(type);
  }

  void writeBool(bool value) {
    asImpl().writeUInt64(uint64_t(value));
  }

  void writeUInt32(uint32_t value) {
    asImpl().writeUInt64(uint64_t(value));
  }

  void writeSelector(clang::Selector selector) {
    if (selector.isNull()) {
      asImpl().writeUInt64(0);
      return;
    }

    asImpl().writeUInt64(selector.getNumArgs() + 1);
    for (unsigned i = 0, e = std::max(selector.getNumArgs(), 1U); i != e; ++i)
      asImpl().writeIdentifier(selector.getIdentifierInfoForSlot(i));
  }

  void writeSourceLocation(clang::SourceLocation loc) {
    // DataStreamBasicReader will always read null; the serializability
    // check overrides this to complain about non-null source locations.
  }

  void writeQualType(clang::QualType type) {
    assert(!type.isNull());

    auto split = type.split();
    asImpl().writeQualifiers(split.Quals);

    // Just recursively visit the given type.
    asImpl().writeTypeRef(split.Ty);
  }
};

}

#endif
