//=== ClangTypeNodesEmitter.cpp - Generate type node tables -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This tblgen backend emits the node table (the .def file) for Clang
// type nodes.
//
// This file defines the AST type info database. Each type node is
// enumerated by providing its name (e.g., "Builtin" or "Enum") and
// base class (e.g., "Type" or "TagType"). Depending on where in the
// abstract syntax tree the type will show up, the enumeration uses
// one of five different macros:
//
//    TYPE(Class, Base) - A type that can show up anywhere in the AST,
//    and might be dependent, canonical, or non-canonical. All clients
//    will need to understand these types.
//
//    ABSTRACT_TYPE(Class, Base) - An abstract class that shows up in
//    the type hierarchy but has no concrete instances.
//
//    NON_CANONICAL_TYPE(Class, Base) - A type that can show up
//    anywhere in the AST but will never be a part of a canonical
//    type. Clients that only need to deal with canonical types
//    (ignoring, e.g., typedefs and other type aliases used for
//    pretty-printing) can ignore these types.
//
//    DEPENDENT_TYPE(Class, Base) - A type that will only show up
//    within a C++ template that has not been instantiated, e.g., a
//    type that is always dependent. Clients that do not need to deal
//    with uninstantiated C++ templates can ignore these types.
//
//    NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) - A type that
//    is non-canonical unless it is dependent.  Defaults to TYPE because
//    it is neither reliably dependent nor reliably non-canonical.
//
// There is a sixth macro, independent of the others.  Most clients
// will not need to use it.
//
//    LEAF_TYPE(Class) - A type that never has inner types.  Clients
//    which can operate on such types more efficiently may wish to do so.
//
//===----------------------------------------------------------------------===//

#include "ClangASTEmitters.h"
#include "TableGenBackends.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <set>
#include <string>
#include <vector>

using namespace llvm;

// These are spellings in the generated output.
#define TypeMacroName "TYPE"
#define AbstractTypeMacroName "ABSTRACT_TYPE"
#define DependentTypeMacroName "DEPENDENT_TYPE"
#define NonCanonicalTypeMacroName "NON_CANONICAL_TYPE"
#define NonCanonicalUnlessDependentTypeMacroName "NON_CANONICAL_UNLESS_DEPENDENT_TYPE"
#define TypeMacroArgs "(Class, Base)"
#define LastTypeMacroName "LAST_TYPE"
#define LeafTypeMacroName "LEAF_TYPE"

#define TypeClassName "Type"

static StringRef getIdForType(Record *type) {
	// The record name is expected to be the full C++ class name,
	// including "Type".  Check for that and strip it off.
	auto fullName = type->getName();
	if (!fullName.endswith("Type"))
		PrintFatalError(type->getLoc(), "name of Type node doesn't end in Type");
	return fullName.drop_back(4);
}

namespace {
class TypeNodeEmitter {
	RecordKeeper &Records;
	raw_ostream &Out;
	const std::vector<Record*> Types;
	std::vector<StringRef> MacrosToUndef;

public:
	TypeNodeEmitter(RecordKeeper &records, raw_ostream &out)
		: Records(records), Out(out),
			Types(Records.getAllDerivedDefinitions(TypeNodeClassName)) {
	}

	void emit();

private:
	void emitFallbackDefine(StringRef macroName, StringRef fallbackMacroName,
													StringRef args);

	void emitNodeInvocations();
	void emitLastNodeInvocation();
	void emitLeafNodeInvocations();

	void addMacroToUndef(StringRef macroName);
	void emitUndefs();
};
}

void TypeNodeEmitter::emit() {
	if (Types.empty())
		PrintFatalError("no Type records in input!");

	emitSourceFileHeader("An x-macro database of Clang type nodes", Out);

	// Preamble
	addMacroToUndef(TypeMacroName);
	addMacroToUndef(AbstractTypeMacroName);
	emitFallbackDefine(AbstractTypeMacroName, TypeMacroName, TypeMacroArgs);
	emitFallbackDefine(NonCanonicalTypeMacroName, TypeMacroName, TypeMacroArgs);
	emitFallbackDefine(DependentTypeMacroName, TypeMacroName, TypeMacroArgs);
	emitFallbackDefine(NonCanonicalUnlessDependentTypeMacroName, TypeMacroName, 
										 TypeMacroArgs);

	// Invocations.
	emitNodeInvocations();
	emitLastNodeInvocation();
	emitLeafNodeInvocations();

	// Postmatter
	emitUndefs();
}

void TypeNodeEmitter::emitFallbackDefine(StringRef macroName,
																				 StringRef fallbackMacroName,
																				 StringRef args) {
  Out << "#ifndef " << macroName << "\n";
  Out << "#  define " << macroName << args
  	  << " " << fallbackMacroName << args << "\n";
  Out << "#endif\n";

  addMacroToUndef(macroName);
}

void TypeNodeEmitter::emitNodeInvocations() {
	for (auto type : Types) {
		// The name without the Type suffix.
		StringRef id = getIdForType(type);

		// If this is the Type node itself, skip it.
		if (id.empty()) continue;

		// Figure out which macro to use.
		StringRef macroName;
		auto setMacroName = [&](StringRef newName) {
			if (!macroName.empty())
				PrintFatalError(type->getLoc(),
												Twine("conflict when computing macro name for "
															"Type node: trying to use both \"")
													+ macroName + "\" and \"" + newName + "\"");
			macroName = newName;
		};
		if (type->isSubClassOf(AlwaysDependentClassName))
			setMacroName(DependentTypeMacroName);
		if (type->isSubClassOf(NeverCanonicalClassName))
			setMacroName(NonCanonicalTypeMacroName);
		if (type->isSubClassOf(NeverCanonicalUnlessDependentClassName))
			setMacroName(NonCanonicalUnlessDependentTypeMacroName);
		if (type->getValueAsBit(AbstractFieldName))
			setMacroName(AbstractTypeMacroName);
		if (macroName.empty())
			macroName = TypeMacroName;

		// Compute the base class.
		StringRef baseName = TypeClassName;
		if (auto base = type->getValueAsOptionalDef(BaseFieldName))
			baseName = base->getName();

		// Generate the invocation line.
		Out << macroName << "(" << id << ", " << baseName << ")\n";
	}
}

void TypeNodeEmitter::emitLastNodeInvocation() {
	// We check that this is non-empty earlier.
	Out << "#ifdef " LastTypeMacroName "\n"
	       LastTypeMacroName "(" << getIdForType(Types.back()) << ")\n"
				 "#undef " LastTypeMacroName "\n"
				 "#endif\n";
}

void TypeNodeEmitter::emitLeafNodeInvocations() {
	Out << "#ifdef " LeafTypeMacroName "\n";

	for (auto type : Types) {
		if (!type->isSubClassOf(LeafTypeClassName)) continue;
		Out << LeafTypeMacroName "(" << getIdForType(type) << ")\n";
	}

	Out << "#undef " LeafTypeMacroName "\n"
				 "#endif\n";
}

void TypeNodeEmitter::addMacroToUndef(StringRef macroName) {
	MacrosToUndef.push_back(macroName);
}

void TypeNodeEmitter::emitUndefs() {
	for (auto &macroName : MacrosToUndef) {
		Out << "#undef " << macroName << "\n";
	}
}

void clang::EmitClangTypeNodes(RecordKeeper &records, raw_ostream &out) {
	TypeNodeEmitter(records, out).emit();
}
