//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// Type.hpp --
//
// Author           : Aaron B. Moss
// Created On       : Thu May 9 10:00:00 2019
// Last Modified By : Aaron B. Moss
// Last Modified On : Thu May 9 10:00:00 2019
// Update Count     : 1
//

#pragma once

#include <cassert>
#include <cstddef>           // for nullptr_t
#include <cstdint>           // for uintptr_t
#include <utility>           // for move
#include <vector>

#include "CVQualifiers.hpp"
#include "Decl.hpp"
#include "Fwd.hpp"
#include "Node.hpp"          // for Node, ptr
#include "TypeVar.hpp"
#include "Visitor.hpp"

namespace ast {

class Type : public Node {
	CV::Qualifiers tq;
public:
	Type( CV::Qualifiers q = {} ) : tq(q) {}

	CV::Qualifiers qualifiers() const { return tq; }
	bool is_const() const { return tq.is_const; }
	bool is_volatile() const { return tq.is_volatile; }
	bool is_restrict() const { return tq.is_restrict; }
	bool is_lvalue() const { return tq.is_lvalue; }
	bool is_mutex() const { return tq.is_mutex; }
	bool is_atomic() const { return tq.is_atomic; }

	void set_qualifiers( CV::Qualifiers q ) { tq = q; }
	void set_const( bool v ) { tq.is_const = v; }
	void set_restrict( bool v ) { tq.is_restrict = v; }
	void set_lvalue( bool v ) { tq.is_lvalue = v; }
	void set_mutex( bool v ) { tq.is_mutex = v; }
	void set_atomic( bool v ) { tq.is_atomic = v; }

	/// How many elemental types are represented by this type
	virtual unsigned size() const { return 1; }
	/// Is this a void type?
	virtual bool isVoid() const { return size() == 0; }
	/// Get the i'th component of this type
	virtual const Type * getComponent( unsigned i );

	/// type without outer pointers and arrays
	const Type * stripDeclarator();
	/// type without outer references
	const Type * stripReferences();
	/// number of reference occuring consecutively on the outermost layer of this type
	/// (i.e. do not count references nested within other types)
	virtual unsigned referenceDepth() const { return 0; }
	/// true iff type is complete type (i.e. compiler knows the size, alignment, and layout)
	virtual bool isComplete() const { return true; }

	virtual const Type * accept( Visitor & v ) const override = 0;
private:
	virtual Type * clone() const override = 0;
};

/// `void`
class VoidType final : public Type {
public:
	VoidType( CV::Qualifiers q = {} ) : Type( q ) {}
	
	unsigned size() const override { return 0; }
	bool isVoid() const override { return true; }
	bool isComplete() const override { return false; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	VoidType * clone() const override { return new VoidType{ *this }; }
};

/// Built-in arithmetic type
class BasicType final : public Type {
public:
	// GENERATED START, DO NOT EDIT
	// GENERATED BY BasicTypes-gen.cc
	enum Kind {
		Bool,
		Char,
		SignedChar,
		UnsignedChar,
		ShortSignedInt,
		ShortUnsignedInt,
		SignedInt,
		UnsignedInt,
		LongSignedInt,
		LongUnsignedInt,
		LongLongSignedInt,
		LongLongUnsignedInt,
		SignedInt128,
		UnsignedInt128,
		uFloat16,
		uFloat16Complex,
		uFloat32,
		uFloat32Complex,
		Float,
		FloatComplex,
		uFloat32x,
		uFloat32xComplex,
		uFloat64,
		uFloat64Complex,
		Double,
		DoubleComplex,
		uFloat64x,
		uFloat64xComplex,
		uuFloat80,
		uFloat128,
		uFloat128Complex,
		uuFloat128,
		LongDouble,
		LongDoubleComplex,
		uFloat128x,
		uFloat128xComplex,
		NUMBER_OF_BASIC_TYPES
	} kind;
	// GENERATED END

	/// xxx -- MAX_INTEGER_TYPE should probably be in BasicTypes-gen.cc, rather than hardcoded here
	enum { MAX_INTEGER_TYPE = UnsignedInt128 };

	/// string names of basic types; generated to match with Kind
	static const char *typeNames[];

	BasicType( Kind k, CV::Qualifiers q = {} ) : Type(q), kind(k) {}

	/// Check if this type represents an integer type
	bool isInteger() const { return (unsigned)kind <= (unsigned)MAX_INTEGER_TYPE; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	BasicType * clone() const override { return new BasicType{ *this }; }
};

/// Pointer/array variable length?
enum LengthFlag { FixedLen, VariableLen };

/// Pointer/array static dimension?
enum DimensionFlag { DynamicDim, StaticDim };

/// Pointer type `T*`
class PointerType final : public Type {
public:
	ptr<Type> base;

	// In C99, pointer types can be qualified in many ways, e.g. `int a[ static 3 ]`
	ptr<Expr> dimension;
	LengthFlag isVarLen = FixedLen;
	DimensionFlag isStatic = DynamicDim;

	PointerType( const Type * b, CV::Qualifiers q = {} ) : Type(q), base(b), dimension() {}
	PointerType( const Type * b, const Expr * d, LengthFlag vl, DimensionFlag s, 
		CV::Qualifiers q = {} ) : Type(q), base(b), dimension(d), isVarLen(vl), isStatic(s) {}

	// true if this pointer is actually an array
	bool isArray() const { return isVarLen || isStatic || dimension; }

	bool isComplete() const override { return ! isVarLen; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	PointerType * clone() const override { return new PointerType{ *this }; }
};

/// Array type `T[]`
class ArrayType final : public Type {
public:
	ptr<Type> base;
	ptr<Expr> dimension;
	LengthFlag isVarLen;
	DimensionFlag isStatic;

	ArrayType( const Type * b, const Expr * d, LengthFlag vl, DimensionFlag s, 
		CV::Qualifiers q = {} ) : Type(q), base(b), dimension(d), isVarLen(vl), isStatic(s) {}

	// array types are complete if they have a dimension expression or are
	// VLAs ('*' in parameter declaration), and incomplete otherwise.
	// See 6.7.6.2
	bool isComplete() const override { return dimension || isVarLen; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	ArrayType * clone() const override { return new ArrayType{ *this }; }
};

/// Reference type `T&`
class ReferenceType final : public Type {
public:
	ptr<Type> base;

	ReferenceType( const Type * b, CV::Qualifiers q = {} ) : Type(q), base(b) {}

	unsigned referenceDepth() const override { return base->referenceDepth() + 1; }

	// Since reference types act like value types, their size is the size of the base.
	// This makes it simple to cast the empty tuple to a reference type, since casts that increase
	// the number of values are disallowed.
	unsigned size() const override { return base->size(); }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	ReferenceType * clone() const override { return new ReferenceType{ *this }; }
};

/// Qualified type `P.C`
class QualifiedType final : public Type {
public:
	ptr<Type> parent;
	ptr<Type> child;

	QualifiedType( const Type * p, const Type * c, CV::Qualifiers q = {} ) 
	: Type(q), parent(p), child(c) {}

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	QualifiedType * clone() const override { return new QualifiedType{ *this }; }
};

/// Base type for potentially forall-qualified types
class ParameterizedType : public Type {
public:
	using ForallList = std::vector<ptr<TypeDecl>>;

	ForallList forall;

	ParameterizedType( ForallList&& fs = {}, CV::Qualifiers q = {} ) 
	: Type(q), forall(std::move(fs)) {}
	ParameterizedType( CV::Qualifiers q ) : Type(q), forall() {}

private:
	virtual ParameterizedType * clone() const override = 0;
};

/// Function variable arguments flag
enum ArgumentFlag { FixedArgs, VariableArgs };

/// Type of a function `[R1, R2](*)(P1, P2, P3)`
class FunctionType final : public ParameterizedType {
public:
	std::vector<ptr<DeclWithType>> returnVals;
	std::vector<ptr<DeclWithType>> parameters;

	/// Does the function accept a variable number of arguments following the arguments specified 
	/// in the parameters list.
	/// This could be because of
	/// - an ellipsis in a prototype declaration
	/// - an unprototyped declaration
	ArgumentFlag isVarArgs;

	FunctionType( ArgumentFlag va = FixedArgs, CV::Qualifiers q = {} )
	: ParameterizedType(q), returnVals(), parameters(), isVarArgs(va) {}

	/// true if either the parameters or return values contain a tttype
	bool isTtype() const;
	/// true if function parameters are unconstrained by prototype
	bool isUnprototyped() const { return isVarArgs && parameters.size() == 0; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	FunctionType * clone() const override { return new FunctionType{ *this }; }
};

/// base class for types that refer to types declared elsewhere (aggregates and typedefs)
class ReferenceToType : public ParameterizedType {
public:
	std::vector<ptr<Expr>> parameters;
	std::vector<ptr<Attribute>> attributes;
	std::string name;
	bool hoistType = false;

	ReferenceToType( const std::string& n, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} )
	: ParameterizedType(q), parameters(), attributes(std::move(as)), name(n) {}

	/// Gets aggregate declaration this type refers to
	virtual const AggregateDecl * aggr() const = 0;
	/// Looks up member declarations with given name
	std::vector<readonly<Decl>> lookup( const std::string & name ) const;

private:
	virtual ReferenceToType * clone() const override = 0;

protected:
	/// Name for the kind of type this is
	virtual std::string typeString() const = 0;
};

/// instance of struct type
class StructInstType final : public ReferenceToType {
public:
	readonly<StructDecl> base;

	StructInstType( const std::string& n, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} )
	: ReferenceToType( n, q, std::move(as) ), base() {}
	StructInstType( const StructDecl * b, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} );
	
	bool isComplete() const override;

	const StructDecl * aggr() const override { return base; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	StructInstType * clone() const override { return new StructInstType{ *this }; }

	std::string typeString() const override { return "struct"; }
};

/// instance of union type
class UnionInstType final : public ReferenceToType {
public:
	readonly<UnionDecl> base;

	UnionInstType( const std::string& n, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} )
	: ReferenceToType( n, q, std::move(as) ), base() {}
	UnionInstType( const UnionDecl * b, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} );
	
	bool isComplete() const override;

	const UnionDecl * aggr() const override { return base; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	UnionInstType * clone() const override { return new UnionInstType{ *this }; }

	std::string typeString() const override { return "union"; }
};

/// instance of enum type
class EnumInstType final : public ReferenceToType {
public:
	readonly<EnumDecl> base;

	EnumInstType( const std::string& n, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} )
	: ReferenceToType( n, q, std::move(as) ), base() {}
	EnumInstType( const EnumDecl * b, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} );
	
	bool isComplete() const override;

	const EnumDecl * aggr() const override { return base; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	EnumInstType * clone() const override { return new EnumInstType{ *this }; }

	std::string typeString() const override { return "enum"; }
};

/// instance of trait type
class TraitInstType final : public ReferenceToType {
public:
	readonly<TraitDecl> base;

	TraitInstType( const std::string& n, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} )
	: ReferenceToType( n, q, std::move(as) ), base() {}
	TraitInstType( const TraitDecl * b, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} );
	
	// not meaningful for TraitInstType
	bool isComplete() const override { assert(false); }

	const TraitDecl * aggr() const override { return base; }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	TraitInstType * clone() const override { return new TraitInstType{ *this }; }

	std::string typeString() const override { return "trait"; }
};

/// instance of named type alias (typedef or variable)
class TypeInstType final : public ReferenceToType {
public:
	readonly<TypeDecl> base;
	TypeVar::Kind kind;

	TypeInstType( const std::string& n, const TypeDecl * b, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} )
	: ReferenceToType( n, q, std::move(as) ), base( b ), kind( b->kind ) {}
	TypeInstType( const std::string& n, TypeVar::Kind k, CV::Qualifiers q = {}, 
		std::vector<ptr<Attribute>> && as = {} )
	: ReferenceToType( n, q, std::move(as) ), base(), kind( k ) {}

	/// sets `base`, updating `kind` correctly
	void set_base( const TypeDecl * );

	bool isComplete() const override;

	// not meaningful for TypeInstType
	const AggregateDecl * aggr() const override { assert(false); }

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	TypeInstType * clone() const override { return new TypeInstType{ *this }; }

	std::string typeString() const override { return "type"; }
};

/// tuple type e.g. `[int, char]`
class TupleType final : public Type {
public:
	std::vector<ptr<Type>> types;
	std::vector<ptr<Decl>> members;

	TupleType( std::vector<ptr<Type>> && ts, CV::Qualifiers q = {} );

	// collection simulation
	using iterator = std::vector<ptr<Type>>::const_iterator;
	iterator begin() const { return types.begin(); }
	iterator end() const { return types.end(); }
	
	unsigned size() const override { return types.size(); }

	const Type * getComponent( unsigned i ) override {
		assertf( i < size(), "TupleType::getComponent: index %d must be less than size %d", 
			i, size() );
		return *(begin()+i);
	}

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	TupleType * clone() const override { return new TupleType{ *this }; }
};

/// Type of unresolved `typeof()` expression
class TypeofType : public Type {
public:
	ptr<Expr> expr;
	enum Kind { Typeof, Basetypeof } kind;

	TypeofType( const Expr * e, Kind k = Typeof, CV::Qualifiers q = {} ) 
	: Type(q), expr(e), kind(k) {}

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	TypeofType * clone() const override { return new TypeofType{ *this }; }
};

/// GCC built-in varargs type
class VarArgsType final : public Type {
public:
	VarArgsType( CV::Qualifiers q = {} ) : Type( q ) {}
	
	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	VarArgsType * clone() const override { return new VarArgsType{ *this }; }
};

/// Type of zero constant `0`
class ZeroType final : public Type {
public:
	ZeroType( CV::Qualifiers q = {} ) : Type( q ) {}
	
	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	ZeroType * clone() const override { return new ZeroType{ *this }; }	
};

/// Type of one constant `1`
class OneType final : public Type {
public:
	OneType( CV::Qualifiers q = {} ) : Type( q ) {}

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	OneType * clone() const override { return new OneType{ *this }; }
};

/// Parent type for scope-qualified types at global scope
class GlobalScopeType final : public Type {
public:
	GlobalScopeType( CV::Qualifiers q = {} ) : Type( q ) {}

	const Type * accept( Visitor & v ) const override { return v.visit( this ); }
private:
	GlobalScopeType * clone() const override { return new GlobalScopeType{ *this }; }
};

//=================================================================================================
/// This disgusting and giant piece of boiler-plate is here to solve a cyclic dependency
/// remove only if there is a better solution.
/// The problem is that ast::ptr< ... > uses increment/decrement which won't work well with
/// forward declarations
inline void increment( const class Type * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class Type * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class VoidType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class VoidType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class BasicType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class BasicType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class PointerType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class PointerType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class ArrayType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class ArrayType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class ReferenceType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class ReferenceType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class QualifiedType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class QualifiedType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class FunctionType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class FunctionType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class ReferenceToType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class ReferenceToType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class StructInstType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class StructInstType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class UnionInstType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class UnionInstType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class EnumInstType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class EnumInstType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class TraitInstType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class TraitInstType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class TypeInstType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class TypeInstType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class TupleType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class TupleType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class TypeofType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class TypeofType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class VarArgsType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class VarArgsType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class ZeroType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class ZeroType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class OneType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class OneType * node, Node::ref_type ref ) { node->decrement( ref ); }
inline void increment( const class GlobalScopeType * node, Node::ref_type ref ) { node->increment( ref ); }
inline void decrement( const class GlobalScopeType * node, Node::ref_type ref ) { node->decrement( ref ); }

}

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
