//
// 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.h --
//
// Author           : Richard C. Bilson
// Created On       : Mon May 18 07:44:20 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Wed Mar  2 17:29:08 2016
// Update Count     : 21
//

#ifndef TYPE_H
#define TYPE_H

#include "SynTree.h"
#include "Visitor.h"
#include "Mutator.h"

class Type {
  public:
	struct Qualifiers {
		Qualifiers(): isConst( false ), isVolatile( false ), isRestrict( false ), isLvalue( false ), isAtomic( false ), isAttribute( false ) {}
		Qualifiers( bool isConst, bool isVolatile, bool isRestrict, bool isLvalue, bool isAtomic, bool isAttribute ): isConst( isConst ), isVolatile( isVolatile ), isRestrict( isRestrict ), isLvalue( isLvalue ), isAtomic( isAtomic ), isAttribute( isAttribute ) {}

		Qualifiers &operator+=( const Qualifiers &other );
		Qualifiers &operator-=( const Qualifiers &other );
		Qualifiers operator+( const Type::Qualifiers &other );
		bool operator==( const Qualifiers &other );
		bool operator!=( const Qualifiers &other );
		bool operator<=( const Qualifiers &other );
		bool operator>=( const Qualifiers &other );
		bool operator<( const Qualifiers &other );
		bool operator>( const Qualifiers &other );
		void print( std::ostream &os, int indent = 0 ) const;

		bool isConst;
		bool isVolatile;
		bool isRestrict;
		bool isLvalue;
		bool isAtomic;
		bool isAttribute;
	};

	Type( const Qualifiers &tq );
	Type( const Type &other );
	virtual ~Type();

	Qualifiers &get_qualifiers() { return tq; }
	bool get_isConst() { return tq.isConst; }
	bool get_isVolatile() { return tq.isVolatile; }
	bool get_isRestrict() { return tq.isRestrict; }
	bool get_isLvalue() { return tq.isLvalue; }
	bool get_isAtomic() { return tq.isAtomic; }
	bool get_isAttribute() { return tq.isAttribute; }
	void set_isConst( bool newValue ) { tq.isConst = newValue; }
	void set_isVolatile( bool newValue ) { tq.isVolatile = newValue; }
	void set_isRestrict( bool newValue ) { tq.isRestrict = newValue; }
	void set_isLvalue( bool newValue ) { tq.isLvalue = newValue; }
	void set_isAtomic( bool newValue ) { tq.isAtomic = newValue; }
	void set_isAttribute( bool newValue ) { tq.isAttribute = newValue; }
	std::list<TypeDecl*>& get_forall() { return forall; }

	virtual Type *clone() const = 0;
	virtual void accept( Visitor &v ) = 0;
	virtual Type *acceptMutator( Mutator &m ) = 0;
	virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
	Qualifiers tq;
	std::list<TypeDecl*> forall;
};

class VoidType : public Type {
  public:
	VoidType( const Type::Qualifiers &tq );

	virtual VoidType *clone() const { return new VoidType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
};

class BasicType : public Type {
  public:
	enum Kind {
		Bool,
		Char,
		SignedChar,
		UnsignedChar,
		ShortSignedInt,
		ShortUnsignedInt,
		SignedInt,
		UnsignedInt,
		LongSignedInt,
		LongUnsignedInt,
		LongLongSignedInt,
		LongLongUnsignedInt,
		Float,
		Double,
		LongDouble,
		FloatComplex,
		DoubleComplex,
		LongDoubleComplex,
		FloatImaginary,
		DoubleImaginary,
		LongDoubleImaginary,
		NUMBER_OF_BASIC_TYPES
	};

	static const char *typeNames[];						// string names for basic types, MUST MATCH with Kind

	BasicType( const Type::Qualifiers &tq, Kind bt );

	Kind get_kind() { return kind; }
	void set_kind( Kind newValue ) { kind = newValue; }

	virtual BasicType *clone() const { return new BasicType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;

	bool isInteger() const;
  private:
	Kind kind;
};

class PointerType : public Type {
  public:
	PointerType( const Type::Qualifiers &tq, Type *base );
	PointerType( const Type::Qualifiers &tq, Type *base, Expression *dimension, bool isVarLen, bool isStatic );
	PointerType( const PointerType& );
	virtual ~PointerType();

	Type *get_base() { return base; }
	void set_base( Type *newValue ) { base = newValue; }
	Expression *get_dimension() { return dimension; }
	void set_dimension( Expression *newValue ) { dimension = newValue; }
	bool get_isVarLen() { return isVarLen; }
	void set_isVarLen( bool newValue ) { isVarLen = newValue; }
	bool get_isStatic() { return isStatic; }
	void set_isStatic( bool newValue ) { isStatic = newValue; }

	virtual PointerType *clone() const { return new PointerType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
	Type *base;

	// In C99, pointer types can be qualified in many ways e.g., int f( int a[ static 3 ] )
	Expression *dimension;
	bool isVarLen;
	bool isStatic;
};

class ArrayType : public Type {
  public:
	ArrayType( const Type::Qualifiers &tq, Type *base, Expression *dimension, bool isVarLen, bool isStatic );
	ArrayType( const ArrayType& );
	virtual ~ArrayType();

	Type *get_base() { return base; }
	void set_base( Type *newValue ) { base = newValue; }
	Expression *get_dimension() { return dimension; }
	void set_dimension( Expression *newValue ) { dimension = newValue; }
	bool get_isVarLen() { return isVarLen; }
	void set_isVarLen( bool newValue ) { isVarLen = newValue; }
	bool get_isStatic() { return isStatic; }
	void set_isStatic( bool newValue ) { isStatic = newValue; }

	virtual ArrayType *clone() const { return new ArrayType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
	Type *base;
	Expression *dimension;
	bool isVarLen;
	bool isStatic;
};

class FunctionType : public Type {
  public:
	FunctionType( const Type::Qualifiers &tq, bool isVarArgs );
	FunctionType( const FunctionType& );
	virtual ~FunctionType();

	std::list<DeclarationWithType*> & get_returnVals() { return returnVals; }
	std::list<DeclarationWithType*> & get_parameters() { return parameters; }
	bool get_isVarArgs() { return isVarArgs; }
	void set_isVarArgs( bool newValue ) { isVarArgs = newValue; }

	virtual FunctionType *clone() const { return new FunctionType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
	std::list<DeclarationWithType*> returnVals;
	std::list<DeclarationWithType*> 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
	bool isVarArgs;
};

class ReferenceToType : public Type {
  public:
	ReferenceToType( const Type::Qualifiers &tq, const std::string &name );
	ReferenceToType( const ReferenceToType &other );
	virtual ~ReferenceToType();

	const std::string &get_name() const { return name; }
	void set_name( std::string newValue ) { name = newValue; }
	std::list< Expression* >& get_parameters() { return parameters; }

	virtual ReferenceToType *clone() const = 0;
	virtual void accept( Visitor &v ) = 0;
	virtual Type *acceptMutator( Mutator &m ) = 0;
	virtual void print( std::ostream &os, int indent = 0 ) const;
  protected:
	virtual std::string typeString() const = 0;
	std::list< Expression* > parameters;
  private:
	std::string name;
};

class StructInstType : public ReferenceToType {
	typedef ReferenceToType Parent;
  public:
	StructInstType( const Type::Qualifiers &tq, const std::string &name ) : Parent( tq, name ), baseStruct( 0 ) {}
	StructInstType( const StructInstType &other ) : Parent( other ), baseStruct( other.baseStruct ) {}

	StructDecl *get_baseStruct() const { return baseStruct; }
	void set_baseStruct( StructDecl *newValue ) { baseStruct = newValue; }

	/// Accesses generic parameters of base struct (NULL if none such)
	std::list<TypeDecl*> * get_baseParameters();

	/// Looks up the members of this struct named "name" and places them into "foundDecls".
	/// Clones declarations into "foundDecls", caller responsible for freeing
	void lookup( const std::string &name, std::list< Declaration* > &foundDecls ) const;

	virtual StructInstType *clone() const { return new StructInstType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
  private:
	virtual std::string typeString() const;

	// this decl is not "owned" by the struct inst; it is merely a pointer to elsewhere in the tree,
	// where the structure used in this type is actually defined
	StructDecl *baseStruct;
};

class UnionInstType : public ReferenceToType {
	typedef ReferenceToType Parent;
  public:
	UnionInstType( const Type::Qualifiers &tq, const std::string &name ) : Parent( tq, name ), baseUnion( 0 ) {}
	UnionInstType( const UnionInstType &other ) : Parent( other ), baseUnion( other.baseUnion ) {}

	UnionDecl *get_baseUnion() const { return baseUnion; }
	void set_baseUnion( UnionDecl *newValue ) { baseUnion = newValue; }

	/// Accesses generic parameters of base union (NULL if none such)
	std::list<TypeDecl*> * get_baseParameters();

	/// looks up the members of this union named "name" and places them into "foundDecls"
	/// Clones declarations into "foundDecls", caller responsible for freeing
	void lookup( const std::string &name, std::list< Declaration* > &foundDecls ) const;

	virtual UnionInstType *clone() const { return new UnionInstType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
  private:
	virtual std::string typeString() const;

	// this decl is not "owned" by the union inst; it is merely a pointer to elsewhere in the tree,
	// where the union used in this type is actually defined
	UnionDecl *baseUnion;
};

class EnumInstType : public ReferenceToType {
	typedef ReferenceToType Parent;
  public:
	EnumInstType( const Type::Qualifiers &tq, const std::string &name ) : Parent( tq, name ) {}
	EnumInstType( const EnumInstType &other ) : Parent( other ) {}

	virtual EnumInstType *clone() const { return new EnumInstType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
  private:
	virtual std::string typeString() const;
};

class TraitInstType : public ReferenceToType {
	typedef ReferenceToType Parent;
  public:
	TraitInstType( const Type::Qualifiers &tq, const std::string &name ) : Parent( tq, name ) {}
	TraitInstType( const TraitInstType &other );
	~TraitInstType();

	std::list< Declaration* >& get_members() { return members; }

	virtual TraitInstType *clone() const { return new TraitInstType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
  private:
	virtual std::string typeString() const;

	// this member is filled in by the validate pass, which instantiates the members of the correponding
	// aggregate with the actual type parameters specified for this use of the context
	std::list< Declaration* > members;
};

class TypeInstType : public ReferenceToType {
	typedef ReferenceToType Parent;
  public:
	TypeInstType( const Type::Qualifiers &tq, const std::string &name, TypeDecl *baseType );
	TypeInstType( const Type::Qualifiers &tq, const std::string &name, bool isFtype );
	TypeInstType( const TypeInstType &other ) : Parent( other ), baseType( other.baseType ), isFtype( other.isFtype ) {}

	TypeDecl *get_baseType() const { return baseType; }
	void set_baseType( TypeDecl *newValue );
	bool get_isFtype() const { return isFtype; }
	void set_isFtype( bool newValue ) { isFtype = newValue; }

	virtual TypeInstType *clone() const { return new TypeInstType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
	virtual std::string typeString() const;
	// this decl is not "owned" by the type inst; it is merely a pointer to elsewhere in the tree,
	// where the type used here is actually defined
	TypeDecl *baseType;
	bool isFtype;
};

class TupleType : public Type {
  public:
	TupleType( const Type::Qualifiers &tq );
	TupleType( const TupleType& );
	virtual ~TupleType();

	std::list<Type*>& get_types() { return types; }

	virtual TupleType *clone() const { return new TupleType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
	std::list<Type*> types;
};

class TypeofType : public Type {
  public:
	TypeofType( const Type::Qualifiers &tq, Expression *expr );
	TypeofType( const TypeofType& );
	virtual ~TypeofType();

	Expression *get_expr() const { return expr; }
	void set_expr( Expression *newValue ) { expr = newValue; }

	virtual TypeofType *clone() const { return new TypeofType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
	Expression *expr;
};

class AttrType : public Type {
  public:
	AttrType( const Type::Qualifiers &tq, const std::string &name, Expression *expr );
	AttrType( const Type::Qualifiers &tq, const std::string &name, Type *type );
	AttrType( const AttrType& );
	virtual ~AttrType();

	const std::string &get_name() const { return name; }
	void set_name( const std::string &newValue ) { name = newValue; }
	Expression *get_expr() const { return expr; }
	void set_expr( Expression *newValue ) { expr = newValue; }
	Type *get_type() const { return type; }
	void set_type( Type *newValue ) { type = newValue; }
	bool get_isType() const { return isType; }
	void set_isType( bool newValue ) { isType = newValue; }

	virtual AttrType *clone() const { return new AttrType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
  private:
	std::string name;
	Expression *expr;
	Type *type;
	bool isType;
};

/// Represents the GCC built-in varargs type
class VarArgsType : public Type {
  public:
	VarArgsType();
	VarArgsType( Type::Qualifiers tq );

	virtual VarArgsType *clone() const { return new VarArgsType( *this ); }
	virtual void accept( Visitor &v ) { v.visit( this ); }
	virtual Type *acceptMutator( Mutator &m ) { return m.mutate( this ); }
	virtual void print( std::ostream &os, int indent = 0 ) const;
};

inline Type::Qualifiers &Type::Qualifiers::operator+=( const Type::Qualifiers &other ) {
	isConst |= other.isConst;
	isVolatile |= other.isVolatile;
	isRestrict |= other.isRestrict;
	isLvalue |= other.isLvalue;
	isAtomic |= other.isAtomic;
	return *this;
}

inline Type::Qualifiers &Type::Qualifiers::operator-=( const Type::Qualifiers &other ) {
	if ( other.isConst ) isConst = 0;
	if ( other.isVolatile ) isVolatile = 0;
	if ( other.isRestrict ) isRestrict = 0;
	if ( other.isAtomic ) isAtomic = 0;
	return *this;
}

inline Type::Qualifiers Type::Qualifiers::operator+( const Type::Qualifiers &other ) {
	Qualifiers q = other;
	q += *this;
	return q;
}

inline bool Type::Qualifiers::operator==( const Qualifiers &other ) {
	return isConst == other.isConst
		&& isVolatile == other.isVolatile
//		&& isRestrict == other.isRestrict
//		&& isLvalue == other.isLvalue
		&& isAtomic == other.isAtomic;
}

inline bool Type::Qualifiers::operator!=( const Qualifiers &other ) {
	return isConst != other.isConst
		|| isVolatile != other.isVolatile
//		|| isRestrict != other.isRestrict
//		|| isLvalue != other.isLvalue
		|| isAtomic != other.isAtomic;
}

inline bool Type::Qualifiers::operator<=( const Type::Qualifiers &other ) {
	return isConst <= other.isConst
		&& isVolatile <= other.isVolatile
//		&& isRestrict <= other.isRestrict
//		&& isLvalue >= other.isLvalue
		&& isAtomic == other.isAtomic;
}

inline bool Type::Qualifiers::operator>=( const Type::Qualifiers &other ) {
	return isConst >= other.isConst
		&& isVolatile >= other.isVolatile
//		&& isRestrict >= other.isRestrict
//		&& isLvalue <= other.isLvalue
		&& isAtomic == other.isAtomic;
}

inline bool Type::Qualifiers::operator<( const Type::Qualifiers &other ) {
	return operator!=( other ) && operator<=( other );
}

inline bool Type::Qualifiers::operator>( const Type::Qualifiers &other ) {
	return operator!=( other ) && operator>=( other );
}

std::ostream & operator<<( std::ostream & out, Type * type );

#endif // TYPE_H

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