//===--- CanTypeVisitor.h - TypeVisitor specialization ----------*- 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 defines CanTypeVisitor, a specialized version of
// TypeVisitor for visiting fully type-checked canonical types.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_AST_CANTYPEVISITOR_H
#define SWIFT_AST_CANTYPEVISITOR_H

#include "swift/AST/Types.h"

namespace swift {

/// This is a specialization of swift::TypeVisitor which:
///   - works only on canonical and fully-checked types and
///   - preserves the canonicality of the visited types in the
///     static (C++) type.
template<typename ImplClass, typename RetTy = void, typename... Args>
class CanTypeVisitor {
public:
  RetTy visit(CanType T, Args... args) {
    switch (T->getKind()) {
#define SUGARED_TYPE(CLASS, PARENT) \
    case TypeKind::CLASS:
#define TYPE(CLASS, PARENT)
#include "swift/AST/TypeNodes.def"
      llvm_unreachable("non-canonical type");

#define SUGARED_TYPE(CLASS, PARENT)
#define TYPE(CLASS, PARENT)                                  \
    case TypeKind::CLASS:                                    \
      return static_cast<ImplClass*>(this)                   \
        ->visit##CLASS##Type(cast<CLASS##Type>(T),           \
                             ::std::forward<Args>(args)...);
#include "swift/AST/TypeNodes.def"
    }
    llvm_unreachable("Not reachable, all cases handled");
  }
  
  // Provide default implementations of abstract "visit" implementations that
  // just chain to their base class.  This allows visitors to just implement
  // the base behavior and handle all subclasses if they desire.  Since this is
  // a template, it will only instantiate cases that are used and thus we still
  // require full coverage of the AST nodes by the visitor.
#define ABSTRACT_TYPE(CLASS, PARENT)                           \
  RetTy visit##CLASS##Type(Can##CLASS##Type T, Args... args) { \
     return static_cast<ImplClass*>(this)                      \
              ->visit##PARENT(T, std::forward<Args>(args)...); \
  }
#define TYPE(CLASS, PARENT) ABSTRACT_TYPE(CLASS, PARENT)
#define ABSTRACT_SUGARED_TYPE(CLASS, PARENT)
#define SUGARED_TYPE(CLASS, PARENT)
  // Don't allow unchecked types by default, but allow visitors to opt-in to
  // handling them.
#define UNCHECKED_TYPE(CLASS, PARENT)                          \
  RetTy visit##CLASS##Type(Can##CLASS##Type T, Args... args) { \
     llvm_unreachable("unchecked type");                       \
  }
#include "swift/AST/TypeNodes.def"
};

} // end namespace swift
  
#endif
