
/* Compiler implementation of the D programming language
 * Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
 * written by Walter Bright
 * https://www.digitalmars.com
 * Distributed under the Boost Software License, Version 1.0.
 * https://www.boost.org/LICENSE_1_0.txt
 * https://github.com/dlang/dmd/blob/master/src/dmd/mtype.h
 */

#pragma once

#include "root/dcompat.h" // for d_size_t

#include "arraytypes.h"
#include "ast_node.h"
#include "globals.h"
#include "visitor.h"

struct Scope;
class AggregateDeclaration;
class Identifier;
class Expression;
class StructDeclaration;
class ClassDeclaration;
class EnumDeclaration;
class TypeInfoDeclaration;
class Dsymbol;
class TemplateInstance;
class TemplateDeclaration;

class TypeBasic;
class Parameter;

// Back end
#ifdef IN_GCC
typedef union tree_node type;
#else
typedef struct TYPE type;
#endif

void semanticTypeInfo(Scope *sc, Type *t);

Type *typeSemantic(Type *t, const Loc &loc, Scope *sc);
Type *merge(Type *type);

enum class TY : uint8_t
{
    Tarray,             // slice array, aka T[]
    Tsarray,            // static array, aka T[dimension]
    Taarray,            // associative array, aka T[type]
    Tpointer,
    Treference,
    Tfunction,
    Tident,
    Tclass,
    Tstruct,
    Tenum,

    Tdelegate,
    Tnone,
    Tvoid,
    Tint8,
    Tuns8,
    Tint16,
    Tuns16,
    Tint32,
    Tuns32,
    Tint64,

    Tuns64,
    Tfloat32,
    Tfloat64,
    Tfloat80,
    Timaginary32,
    Timaginary64,
    Timaginary80,
    Tcomplex32,
    Tcomplex64,
    Tcomplex80,

    Tbool,
    Tchar,
    Twchar,
    Tdchar,
    Terror,
    Tinstance,
    Ttypeof,
    Ttuple,
    Tslice,
    Treturn,

    Tnull,
    Tvector,
    Tint128,
    Tuns128,
    Ttraits,
    Tmixin,
    Tnoreturn,
    TMAX
};

#define SIZE_INVALID (~(uinteger_t)0)   // error return from size() functions


/**
 * type modifiers
 * pick this order of numbers so switch statements work better
 */
enum MODFlags
{
    MODnone      = 0, // default (mutable)
    MODconst     = 1, // type is const
    MODimmutable = 4, // type is immutable
    MODshared    = 2, // type is shared
    MODwild      = 8, // type is wild
    MODwildconst = (MODwild | MODconst), // type is wild const
    MODmutable   = 0x10       // type is mutable (only used in wildcard matching)
};
typedef unsigned char MOD;

enum VarArgValues
{
    VARARGnone     = 0,  /// fixed number of arguments
    VARARGvariadic = 1,  /// T t, ...)  can be C-style (core.stdc.stdarg) or D-style (core.vararg)
    VARARGtypesafe = 2   /// T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions
                         ///   or https://dlang.org/spec/function.html#typesafe_variadic_functions
};
typedef unsigned char VarArg;

enum class Covariant
{
    distinct = 0, /// types are distinct
    yes = 1,      /// types are covariant
    no = 2,       /// arguments match as far as overloading goes, but types are not covariant
    fwdref = 3,   /// cannot determine covariance because of forward references
};

class Type : public ASTNode
{
public:
    TY ty;
    MOD mod;  // modifiers MODxxxx
    char *deco;

private:
    void* mcache;

public:
    Type *pto;          // merged pointer to this type
    Type *rto;          // reference to this type
    Type *arrayof;      // array of this type
    TypeInfoDeclaration *vtinfo;        // TypeInfo object for this Type

    type *ctype;        // for back end

    static Type *tvoid;
    static Type *tint8;
    static Type *tuns8;
    static Type *tint16;
    static Type *tuns16;
    static Type *tint32;
    static Type *tuns32;
    static Type *tint64;
    static Type *tuns64;
    static Type *tint128;
    static Type *tuns128;
    static Type *tfloat32;
    static Type *tfloat64;
    static Type *tfloat80;

    static Type *timaginary32;
    static Type *timaginary64;
    static Type *timaginary80;

    static Type *tcomplex32;
    static Type *tcomplex64;
    static Type *tcomplex80;

    static Type *tbool;
    static Type *tchar;
    static Type *twchar;
    static Type *tdchar;

    // Some special types
    static Type *tshiftcnt;
    static Type *tvoidptr;              // void*
    static Type *tstring;               // immutable(char)[]
    static Type *twstring;              // immutable(wchar)[]
    static Type *tdstring;              // immutable(dchar)[]
    static Type *terror;                // for error recovery
    static Type *tnull;                 // for null type
    static Type *tnoreturn;             // for bottom type typeof(*null)

    static Type *tsize_t;               // matches size_t alias
    static Type *tptrdiff_t;            // matches ptrdiff_t alias
    static Type *thash_t;               // matches hash_t alias

    static ClassDeclaration *dtypeinfo;
    static ClassDeclaration *typeinfoclass;
    static ClassDeclaration *typeinfointerface;
    static ClassDeclaration *typeinfostruct;
    static ClassDeclaration *typeinfopointer;
    static ClassDeclaration *typeinfoarray;
    static ClassDeclaration *typeinfostaticarray;
    static ClassDeclaration *typeinfoassociativearray;
    static ClassDeclaration *typeinfovector;
    static ClassDeclaration *typeinfoenum;
    static ClassDeclaration *typeinfofunction;
    static ClassDeclaration *typeinfodelegate;
    static ClassDeclaration *typeinfotypelist;
    static ClassDeclaration *typeinfoconst;
    static ClassDeclaration *typeinfoinvariant;
    static ClassDeclaration *typeinfoshared;
    static ClassDeclaration *typeinfowild;

    static TemplateDeclaration *rtinfo;

    static Type *basic[(int)TY::TMAX];

    virtual const char *kind();
    Type *copy() const;
    virtual Type *syntaxCopy();
    bool equals(const RootObject * const o) const override;
    bool equivalent(Type *t);
    // kludge for template.isType()
    DYNCAST dyncast() const override final { return DYNCAST_TYPE; }
    size_t getUniqueID() const;
    Covariant covariant(Type *, StorageClass * = NULL, bool = false);
    const char *toChars() const override;
    char *toPrettyChars(bool QualifyTypes = false);
    static void _init();

    uinteger_t size();
    virtual uinteger_t size(const Loc &loc);
    virtual unsigned alignsize();
    Type *trySemantic(const Loc &loc, Scope *sc);
    Type *merge2();
    void modToBuffer(OutBuffer *buf) const;
    char *modToChars() const;

    virtual bool isintegral();
    virtual bool isfloating();   // real, imaginary, or complex
    virtual bool isreal();
    virtual bool isimaginary();
    virtual bool iscomplex();
    virtual bool isscalar();
    virtual bool isunsigned();
    virtual bool isscope();
    virtual bool isString();
    virtual bool isAssignable();
    virtual bool isBoolean();
    virtual void checkDeprecated(const Loc &loc, Scope *sc);
    bool isConst() const       { return (mod & MODconst) != 0; }
    bool isImmutable() const   { return (mod & MODimmutable) != 0; }
    bool isMutable() const     { return (mod & (MODconst | MODimmutable | MODwild)) == 0; }
    bool isShared() const      { return (mod & MODshared) != 0; }
    bool isSharedConst() const { return (mod & (MODshared | MODconst)) == (MODshared | MODconst); }
    bool isWild() const        { return (mod & MODwild) != 0; }
    bool isWildConst() const   { return (mod & MODwildconst) == MODwildconst; }
    bool isSharedWild() const  { return (mod & (MODshared | MODwild)) == (MODshared | MODwild); }
    bool isNaked() const       { return mod == 0; }
    Type *nullAttributes() const;
    Type *constOf();
    Type *immutableOf();
    Type *mutableOf();
    Type *sharedOf();
    Type *sharedConstOf();
    Type *unSharedOf();
    Type *wildOf();
    Type *wildConstOf();
    Type *sharedWildOf();
    Type *sharedWildConstOf();
    void fixTo(Type *t);
    void check();
    Type *addSTC(StorageClass stc);
    Type *castMod(MOD mod);
    Type *addMod(MOD mod);
    virtual Type *addStorageClass(StorageClass stc);
    Type *pointerTo();
    Type *referenceTo();
    Type *arrayOf();
    Type *sarrayOf(dinteger_t dim);
    bool hasDeprecatedAliasThis();
    Type *aliasthisOf();
    virtual Type *makeConst();
    virtual Type *makeImmutable();
    virtual Type *makeShared();
    virtual Type *makeSharedConst();
    virtual Type *makeWild();
    virtual Type *makeWildConst();
    virtual Type *makeSharedWild();
    virtual Type *makeSharedWildConst();
    virtual Type *makeMutable();
    virtual Dsymbol *toDsymbol(Scope *sc);
    Type *toBasetype();
    virtual bool isBaseOf(Type *t, int *poffset);
    virtual MATCH implicitConvTo(Type *to);
    virtual MATCH constConv(Type *to);
    virtual unsigned char deduceWild(Type *t, bool isRef);
    virtual Type *substWildTo(unsigned mod);

    Type *unqualify(unsigned m);

    virtual Type *toHeadMutable();
    virtual ClassDeclaration *isClassHandle();
    virtual structalign_t alignment();
    virtual Expression *defaultInitLiteral(const Loc &loc);
    virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0
    Identifier *getTypeInfoIdent();
    virtual int hasWild() const;
    virtual bool hasPointers();
    virtual bool hasVoidInitPointers();
    virtual bool hasSystemFields();
    virtual bool hasInvariant();
    virtual Type *nextOf();
    Type *baseElemOf();
    uinteger_t sizemask();
    virtual bool needsDestruction();
    virtual bool needsCopyOrPostblit();
    virtual bool needsNested();

    TypeFunction *toTypeFunction();

    // For eliminating dynamic_cast
    virtual TypeBasic *isTypeBasic();
    TypeFunction *isPtrToFunction();
    TypeFunction *isFunction_Delegate_PtrToFunction();
    TypeError *isTypeError();
    TypeVector *isTypeVector();
    TypeSArray *isTypeSArray();
    TypeDArray *isTypeDArray();
    TypeAArray *isTypeAArray();
    TypePointer *isTypePointer();
    TypeReference *isTypeReference();
    TypeFunction *isTypeFunction();
    TypeDelegate *isTypeDelegate();
    TypeIdentifier *isTypeIdentifier();
    TypeInstance *isTypeInstance();
    TypeTypeof *isTypeTypeof();
    TypeReturn *isTypeReturn();
    TypeStruct *isTypeStruct();
    TypeEnum *isTypeEnum();
    TypeClass *isTypeClass();
    TypeTuple *isTypeTuple();
    TypeSlice *isTypeSlice();
    TypeNull *isTypeNull();
    TypeMixin *isTypeMixin();
    TypeTraits *isTypeTraits();
    TypeNoreturn *isTypeNoreturn();
    TypeTag *isTypeTag();

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeError final : public Type
{
public:
    const char *kind() override;
    TypeError *syntaxCopy() override;

    uinteger_t size(const Loc &loc) override;
    Expression *defaultInitLiteral(const Loc &loc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeNext : public Type
{
public:
    Type *next;

    void checkDeprecated(const Loc &loc, Scope *sc) override final;
    int hasWild() const override final;
    Type *nextOf() override final;
    Type *makeConst() override final;
    Type *makeImmutable() override final;
    Type *makeShared() override final;
    Type *makeSharedConst() override final;
    Type *makeWild() override final;
    Type *makeWildConst() override final;
    Type *makeSharedWild() override final;
    Type *makeSharedWildConst() override final;
    Type *makeMutable() override final;
    MATCH constConv(Type *to) override;
    unsigned char deduceWild(Type *t, bool isRef) override final;
    void transitive();
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeBasic final : public Type
{
public:
    const char *dstring;
    unsigned flags;

    const char *kind() override;
    TypeBasic *syntaxCopy() override;
    uinteger_t size(const Loc &loc) override;
    unsigned alignsize() override;
    bool isintegral() override;
    bool isfloating() override;
    bool isreal() override;
    bool isimaginary() override;
    bool iscomplex() override;
    bool isscalar() override;
    bool isunsigned() override;
    MATCH implicitConvTo(Type *to) override;
    bool isZeroInit(const Loc &loc) override;

    // For eliminating dynamic_cast
    TypeBasic *isTypeBasic() override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeVector final : public Type
{
public:
    Type *basetype;

    static TypeVector *create(Type *basetype);
    const char *kind() override;
    TypeVector *syntaxCopy() override;
    uinteger_t size(const Loc &loc) override;
    unsigned alignsize() override;
    bool isintegral() override;
    bool isfloating() override;
    bool isscalar() override;
    bool isunsigned() override;
    bool isBoolean() override;
    MATCH implicitConvTo(Type *to) override;
    Expression *defaultInitLiteral(const Loc &loc) override;
    TypeBasic *elementType();
    bool isZeroInit(const Loc &loc) override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeArray : public TypeNext
{
public:
    void accept(Visitor *v) override { v->visit(this); }
};

// Static array, one with a fixed dimension
class TypeSArray final : public TypeArray
{
public:
    Expression *dim;

    const char *kind() override;
    TypeSArray *syntaxCopy() override;
    bool isIncomplete();
    uinteger_t size(const Loc &loc) override;
    unsigned alignsize() override;
    bool isString() override;
    bool isZeroInit(const Loc &loc) override;
    structalign_t alignment() override;
    MATCH constConv(Type *to) override;
    MATCH implicitConvTo(Type *to) override;
    Expression *defaultInitLiteral(const Loc &loc) override;
    bool hasPointers() override;
    bool hasSystemFields() override;
    bool hasVoidInitPointers() override;
    bool hasInvariant() override;
    bool needsDestruction() override;
    bool needsCopyOrPostblit() override;
    bool needsNested() override;

    void accept(Visitor *v) override { v->visit(this); }
};

// Dynamic array, no dimension
class TypeDArray final : public TypeArray
{
public:
    const char *kind() override;
    TypeDArray *syntaxCopy() override;
    uinteger_t size(const Loc &loc) override;
    unsigned alignsize() override;
    bool isString() override;
    bool isZeroInit(const Loc &loc) override;
    bool isBoolean() override;
    MATCH implicitConvTo(Type *to) override;
    bool hasPointers() override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeAArray final : public TypeArray
{
public:
    Type *index;                // key type
    Loc loc;

    static TypeAArray *create(Type *t, Type *index);
    const char *kind() override;
    TypeAArray *syntaxCopy() override;
    uinteger_t size(const Loc &loc) override;
    bool isZeroInit(const Loc &loc) override;
    bool isBoolean() override;
    bool hasPointers() override;
    MATCH implicitConvTo(Type *to) override;
    MATCH constConv(Type *to) override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypePointer final : public TypeNext
{
public:
    static TypePointer *create(Type *t);
    const char *kind() override;
    TypePointer *syntaxCopy() override;
    uinteger_t size(const Loc &loc) override;
    MATCH implicitConvTo(Type *to) override;
    MATCH constConv(Type *to) override;
    bool isscalar() override;
    bool isZeroInit(const Loc &loc) override;
    bool hasPointers() override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeReference final : public TypeNext
{
public:
    const char *kind() override;
    TypeReference *syntaxCopy() override;
    uinteger_t size(const Loc &loc) override;
    bool isZeroInit(const Loc &loc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

enum RET
{
    RETregs     = 1,    // returned in registers
    RETstack    = 2     // returned on stack
};

enum class TRUST : unsigned char
{
    default_ = 0,
    system = 1,    // @system (same as TRUSTdefault)
    trusted = 2,   // @trusted
    safe = 3       // @safe
};

enum TRUSTformat
{
    TRUSTformatDefault,  // do not emit @system when trust == TRUSTdefault
    TRUSTformatSystem    // emit @system when trust == TRUSTdefault
};

enum class PURE : unsigned char
{
    impure = 0,     // not pure at all
    fwdref = 1,     // it's pure, but not known which level yet
    weak = 2,       // no mutable globals are read or written
    const_ = 3,     // parameters are values or const
};

class Parameter final : public ASTNode
{
public:
    StorageClass storageClass;
    Type *type;
    Identifier *ident;
    Expression *defaultArg;
    UserAttributeDeclaration *userAttribDecl;   // user defined attributes

    static Parameter *create(StorageClass storageClass, Type *type, Identifier *ident,
                             Expression *defaultArg, UserAttributeDeclaration *userAttribDecl);
    Parameter *syntaxCopy();
    Type *isLazyArray();
    bool isLazy() const;
    bool isReference() const;
    // kludge for template.isType()
    DYNCAST dyncast() const override { return DYNCAST_PARAMETER; }
    void accept(Visitor *v) override { v->visit(this); }

    static size_t dim(Parameters *parameters);
    static Parameter *getNth(Parameters *parameters, d_size_t nth);
    const char *toChars() const override;
    bool isCovariant(bool returnByRef, const Parameter *p, bool previewIn) const;
};

struct ParameterList
{
    Parameters* parameters;
    StorageClass stc;
    VarArg varargs;
    bool hasIdentifierList; // true if C identifier-list style

    size_t length();
    Parameter *operator[](size_t i) { return Parameter::getNth(parameters, i); }
};

class TypeFunction final : public TypeNext
{
public:
    // .next is the return type

    ParameterList parameterList; // function parameters
    uint16_t bitFields;
    LINK linkage;                // calling convention
    TRUST trust;                 // level of trust
    PURE purity;                 // PURExxxx
    char inuse;
    Expressions *fargs;          // function arguments

    static TypeFunction *create(Parameters *parameters, Type *treturn, VarArg varargs, LINK linkage, StorageClass stc = 0);
    const char *kind() override;
    TypeFunction *syntaxCopy() override;
    void purityLevel();
    bool hasLazyParameters();
    bool isDstyleVariadic() const;
    StorageClass parameterStorageClass(Parameter *p);
    Type *addStorageClass(StorageClass stc) override;

    Type *substWildTo(unsigned mod) override;
    MATCH constConv(Type *to) override;

    bool isnothrow() const;
    void isnothrow(bool v);
    bool isnogc() const;
    void isnogc(bool v);
    bool isproperty() const;
    void isproperty(bool v);
    bool isref() const;
    void isref(bool v);
    bool isreturn() const;
    void isreturn(bool v);
    bool isreturnscope() const;
    void isreturnscope(bool v);
    bool isScopeQual() const;
    void isScopeQual(bool v);
    bool isreturninferred() const;
    void isreturninferred(bool v);
    bool isscopeinferred() const;
    void isscopeinferred(bool v);
    bool islive() const;
    void islive(bool v);
    bool incomplete() const;
    void incomplete(bool v);
    bool isInOutParam() const;
    void isInOutParam(bool v);
    bool isInOutQual() const;
    void isInOutQual(bool v);
    bool iswild() const;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeDelegate final : public TypeNext
{
public:
    // .next is a TypeFunction

    static TypeDelegate *create(TypeFunction *t);
    const char *kind() override;
    TypeDelegate *syntaxCopy() override;
    Type *addStorageClass(StorageClass stc) override;
    uinteger_t size(const Loc &loc) override;
    unsigned alignsize() override;
    MATCH implicitConvTo(Type *to) override;
    bool isZeroInit(const Loc &loc) override;
    bool isBoolean() override;
    bool hasPointers() override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeTraits final : public Type
{
    Loc loc;
    /// The expression to resolve as type or symbol.
    TraitsExp *exp;
    /// Cached type/symbol after semantic analysis.
    RootObject *obj;

    const char *kind() override;
    TypeTraits *syntaxCopy() override;
    uinteger_t size(const Loc &loc) override;
    Dsymbol *toDsymbol(Scope *sc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeMixin final : public Type
{
    Loc loc;
    Expressions *exps;
    RootObject *obj;

    const char *kind() override;
    TypeMixin *syntaxCopy() override;
    Dsymbol *toDsymbol(Scope *sc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeQualified : public Type
{
public:
    Loc loc;
    // array of Identifier and TypeInstance,
    // representing ident.ident!tiargs.ident. ... etc.
    Objects idents;

    void syntaxCopyHelper(TypeQualified *t);
    void addIdent(Identifier *ident);
    void addInst(TemplateInstance *inst);
    void addIndex(RootObject *expr);
    uinteger_t size(const Loc &loc) override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeIdentifier final : public TypeQualified
{
public:
    Identifier *ident;
    Dsymbol *originalSymbol; // The symbol representing this identifier, before alias resolution

    static TypeIdentifier *create(const Loc &loc, Identifier *ident);
    const char *kind() override;
    TypeIdentifier *syntaxCopy() override;
    Dsymbol *toDsymbol(Scope *sc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

/* Similar to TypeIdentifier, but with a TemplateInstance as the root
 */
class TypeInstance final : public TypeQualified
{
public:
    TemplateInstance *tempinst;

    const char *kind() override;
    TypeInstance *syntaxCopy() override;
    Dsymbol *toDsymbol(Scope *sc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeTypeof final : public TypeQualified
{
public:
    Expression *exp;
    int inuse;

    const char *kind() override;
    TypeTypeof *syntaxCopy() override;
    Dsymbol *toDsymbol(Scope *sc) override;
    uinteger_t size(const Loc &loc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeReturn final : public TypeQualified
{
public:
    const char *kind() override;
    TypeReturn *syntaxCopy() override;
    Dsymbol *toDsymbol(Scope *sc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

// Whether alias this dependency is recursive or not.
enum AliasThisRec
{
    RECno = 0,      // no alias this recursion
    RECyes = 1,     // alias this has recursive dependency
    RECfwdref = 2,  // not yet known
    RECtypeMask = 3,// mask to read no/yes/fwdref

    RECtracing = 0x4, // mark in progress of implicitConvTo/deduceWild
    RECtracingDT = 0x8  // mark in progress of deduceType
};

class TypeStruct final : public Type
{
public:
    StructDeclaration *sym;
    AliasThisRec att;
    bool inuse;

    static TypeStruct *create(StructDeclaration *sym);
    const char *kind() override;
    uinteger_t size(const Loc &loc) override;
    unsigned alignsize() override;
    TypeStruct *syntaxCopy() override;
    Dsymbol *toDsymbol(Scope *sc) override;
    structalign_t alignment() override;
    Expression *defaultInitLiteral(const Loc &loc) override;
    bool isZeroInit(const Loc &loc) override;
    bool isAssignable() override;
    bool isBoolean() override;
    bool needsDestruction() override;
    bool needsCopyOrPostblit() override;
    bool needsNested() override;
    bool hasPointers() override;
    bool hasVoidInitPointers() override;
    bool hasSystemFields() override;
    bool hasInvariant() override;
    MATCH implicitConvTo(Type *to) override;
    MATCH constConv(Type *to) override;
    unsigned char deduceWild(Type *t, bool isRef) override;
    Type *toHeadMutable() override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeEnum final : public Type
{
public:
    EnumDeclaration *sym;

    const char *kind() override;
    TypeEnum *syntaxCopy() override;
    uinteger_t size(const Loc &loc) override;
    unsigned alignsize() override;
    Type *memType(const Loc &loc = Loc());
    Dsymbol *toDsymbol(Scope *sc) override;
    bool isintegral() override;
    bool isfloating() override;
    bool isreal() override;
    bool isimaginary() override;
    bool iscomplex() override;
    bool isscalar() override;
    bool isunsigned() override;
    bool isBoolean() override;
    bool isString() override;
    bool isAssignable() override;
    bool needsDestruction() override;
    bool needsCopyOrPostblit() override;
    bool needsNested() override;
    MATCH implicitConvTo(Type *to) override;
    MATCH constConv(Type *to) override;
    bool isZeroInit(const Loc &loc) override;
    bool hasPointers() override;
    bool hasVoidInitPointers() override;
    bool hasSystemFields() override;
    bool hasInvariant() override;
    Type *nextOf() override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeClass final : public Type
{
public:
    ClassDeclaration *sym;
    AliasThisRec att;
    CPPMANGLE cppmangle;

    const char *kind() override;
    uinteger_t size(const Loc &loc) override;
    TypeClass *syntaxCopy() override;
    Dsymbol *toDsymbol(Scope *sc) override;
    ClassDeclaration *isClassHandle() override;
    bool isBaseOf(Type *t, int *poffset) override;
    MATCH implicitConvTo(Type *to) override;
    MATCH constConv(Type *to) override;
    unsigned char deduceWild(Type *t, bool isRef) override;
    Type *toHeadMutable() override;
    bool isZeroInit(const Loc &loc) override;
    bool isscope() override;
    bool isBoolean() override;
    bool hasPointers() override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeTuple final : public Type
{
public:
    // 'logically immutable' cached global - don't modify (neither pointer nor pointee)!
    static TypeTuple *empty;

    Parameters *arguments;      // types making up the tuple

    static TypeTuple *create(Parameters *arguments);
    static TypeTuple *create();
    static TypeTuple *create(Type *t1);
    static TypeTuple *create(Type *t1, Type *t2);
    const char *kind() override;
    TypeTuple *syntaxCopy() override;
    bool equals(const RootObject * const o) const override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeSlice final : public TypeNext
{
public:
    Expression *lwr;
    Expression *upr;

    const char *kind() override;
    TypeSlice *syntaxCopy() override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeNull final : public Type
{
public:
    const char *kind() override;

    TypeNull *syntaxCopy() override;
    MATCH implicitConvTo(Type *to) override;
    bool hasPointers() override;
    bool isBoolean() override;

    uinteger_t size(const Loc &loc) override;
    void accept(Visitor *v) override { v->visit(this); }
};

class TypeNoreturn final : public Type
{
public:
    const char *kind() override;
    TypeNoreturn *syntaxCopy() override;
    MATCH implicitConvTo(Type* to) override;
    MATCH constConv(Type* to) override;
    bool isBoolean() override;
    uinteger_t size(const Loc& loc) override;
    unsigned alignsize() override;

    void accept(Visitor *v) override { v->visit(this); }
};

class TypeTag final : public Type
{
public:
    TypeTag *syntaxCopy() override;

    void accept(Visitor *v) override { v->visit(this); }
};

/**************************************************************/

bool arrayTypeCompatibleWithoutCasting(Type *t1, Type *t2);

// If the type is a class or struct, returns the symbol for it, else null.
AggregateDeclaration *isAggregate(Type *t);
