//
// 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 : Peter A. Buhr
// Last Modified On : Wed Dec 11 21:56:46 2019
// Update Count     : 5
//

#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"          // for AggregateDecl subclasses
#include "Fwd.hpp"
#include "Node.hpp"          // for Node, ptr, ptr_base
#include "Visitor.hpp"

// Must be included in *all* AST classes; should be #undef'd at the end of the file
#define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);

namespace ast {

class Type : public Node {
public:
	CV::Qualifiers qualifiers;
	std::vector<ptr<Attribute>> attributes;

	Type( CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
	: qualifiers(q), attributes(std::move(as)) {}

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

	Type * set_const( bool v ) { qualifiers.is_const = v; return this; }
	Type * set_volatile( bool v ) { qualifiers.is_volatile = v; return this; }
	Type * set_restrict( bool v ) { qualifiers.is_restrict = v; return this; }
	Type * set_lvalue( bool v ) { qualifiers.is_lvalue = v; return this; }
	Type * set_mutex( bool v ) { qualifiers.is_mutex = v; return this; }
	Type * set_atomic( bool v ) { qualifiers.is_atomic = v; return this; }

	/// 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 ) const;

	/// type without outer pointers and arrays
	const Type * stripDeclarator() const;
	/// type without outer references
	const Type * stripReferences() const;
	/// 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;
	MUTATE_FRIEND
};

/// Clear/reset the qualifiers on this type, cloning only if necessary
template< enum Node::ref_type ref_t >
void reset_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q = {} ) {
	if ( p->qualifiers.val != q.val ) p.get_and_mutate()->qualifiers = q;
}

/// Add the specified qualifiers to this type, cloning only if necessary
template< enum Node::ref_type ref_t >
void add_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q ) {
	if ( ( p->qualifiers.val & q.val ) != q.val ) p.get_and_mutate()->qualifiers |= q;
}

/// Remove the specified qualifiers from this type, cloning only if necessary
template< enum Node::ref_type ref_t >
void remove_qualifiers( ptr_base< Type, ref_t > & p, CV::Qualifiers q ) {
	if ( ( p->qualifiers.val & q.val ) != 0 ) p.get_and_mutate()->qualifiers -= q;
}

/// `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 }; }
	MUTATE_FRIEND
};

/// 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 = {}, std::vector<ptr<Attribute>> && as = {} ) 
	: Type(q, std::move(as)), 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 = {},
		std::vector<ptr<Attribute>> && as = {} )
	: Type(q, std::move(as)), forall(std::move(fs)) {}

	ParameterizedType( CV::Qualifiers q, std::vector<ptr<Attribute>> && as = {} )
	: Type(q, std::move(as)), forall() {}

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

/// 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>> returns;
	std::vector<ptr<DeclWithType>> params;

	/// 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), returns(), params(), 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 && params.size() == 0; }

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

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

	ReferenceToType( const std::string& n, CV::Qualifiers q = {},
		std::vector<ptr<Attribute>> && as = {} )
	: ParameterizedType(q, std::move(as)), params(), 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;
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// instance of named type alias (typedef or variable)
class TypeInstType final : public ReferenceToType {
public:
	readonly<TypeDecl> base;
	TypeDecl::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, TypeDecl::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 }; }
	MUTATE_FRIEND
};

/// 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 ) const 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

/// 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 }; }
	MUTATE_FRIEND
};

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

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

}

#undef MUTATE_FRIEND

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