// Checks if two types are equal in the sense of the language.
// That is primitive types are equal if they are the same kind.
// Structures are equal if they come from the same struct definition.
// Arrays are equal if their cell types are equal.
bool equivalent(const ty *t1, const ty *t2);
// If special is true, this is the same as above. If special is false, just
// the signatures are compared.
bool equivalent(const ty *t1, const ty *t2, bool special);
class caster {
public:
virtual ~caster() {}
virtual trans::access *operator() (ty *target, ty *source) = 0;
virtual bool castable(ty *target, ty *source) = 0;
};
class ty : public gc {
public:
const ty_kind kind;
ty(ty_kind kind)
: kind(kind) {}
virtual ~ty();
// Returns true if the type is a user-defined type or the null type.
// While the pair, path, etc. are stored by reference, this is
// transparent to the user.
virtual bool isReference() {
return true;
}
// The following are only used by the overloaded type, but it is so common
// to test for an overloaded type then iterate over its types, that this
// allows the code:
// if (t->isOverloaded()) {
// for (ty_iterator i = t->begin(); i != t->end(); ++i) {
// ...
// }
// }
// For speed reasons, only begin has an assert to test if t is overloaded.
bool isOverloaded() const {
return kind == ty_overloaded;
}
bool isNotOverloaded() const { return !isOverloaded(); }
ty_iterator begin();
ty_iterator end();
// If a default initializer is not stored in the environment, the abstract
// syntax asks the type if it has a "default" default initializer, by calling
// this method.
virtual trans::access *initializer() {
return 0;
}
// If a cast function is not stored in the environment, ask the type itself.
// This handles null->record casting, and the like. The caster is used as a
// callback to the environment for casts of subtypes.
virtual trans::access *castTo(ty *, caster &) {
return 0;
}
// Just checks if a cast is possible.
virtual bool castable(ty *target, caster &c) {
return castTo(target, c);
}
// For pair's x and y, and array's length, this is a special type of
// "field".
// In actually, it returns a function which takes the object as its
// parameter and returns the necessary result.
// These should not have public permission, as modifying them would
// have strange results.
virtual trans::varEntry *virtualField(symbol, signature *) {
return 0;
}
// varGetType for virtual fields.
// Unless you are using functions for virtual fields, the base implementation
// should work fine.
virtual ty *virtualFieldGetType(symbol id);
#if 0
// Returns the type. In case of functions, return the equivalent type
// but with no default values for parameters.
virtual ty *stripDefaults()
{
return this;
}
#endif
// Returns true if the other type is equivalent to this one.
// The general function equivalent should be preferably used, as it properly
// handles overloaded type comparisons.
virtual bool equiv(const ty *other) const
{
return this==other;
}
// Returns a number for the type for use in a hash table. Equivalent types
// must yield the same number.
virtual size_t hash() const = 0;
};
class primitiveTy : public ty {
public:
primitiveTy(ty_kind kind)
: ty(kind) {}
bool primitive() {
return true;
}
bool isReference() {
return false;
}
ty *virtualFieldGetType(symbol );
trans::varEntry *virtualField(symbol, signature *);
bool equiv(const ty *other) const
{
return this->kind==other->kind;
}
// string->symbol translation is costly if done too many times. This
// constructor has been disabled to make this cost more visible to the
// programmer.
#if 0
formal(ty *t,
const char *name,
bool optional=false,
bool Explicit=false)
: t(t), name(symbol::trans(name)),
defval(optional ? absyntax::Default : 0), Explicit(Explicit) {}
#endif
// Holds the parameters of a function and if they have default values
// (only applicable in some cases).
struct signature : public gc {
formal_vector formals;
// The number of keyword-only formals. These formals always come after the
// regular formals.
size_t numKeywordOnly;
// Formal for the rest parameter. If there is no rest parameter, then the
// type is null.
formal rest;
// Check if a signature of argument types (as opposed to formal parameters)
// are equivalent. Here, the arguments, if named, must have the same names,
// and (for simplicity) no overloaded arguments are allowed.
friend bool argumentEquivalent(const signature *s1, const signature *s2);
#if 0
friend bool castable(signature *target, signature *source);
friend Int numFormalsMatch(signature *s1, signature *s2);
#endif
size_t hash() const;
// Return a unique handle for this signature
size_t handle();
};
struct function : public ty {
ty *result;
signature sig;
// Only add a type distinct from the ones currently in the overloaded type.
// If special is false, just the distinct signatures are added.
void addDistinct(ty *t, bool special=false);
// If there are less than two overloaded types, the type isn't really
// overloaded. This gives a more appropriate type in this case.
ty *simplify() {
switch (sub.size()) {
case 0:
return 0;
case 1: {
return sub.front();
}
default:
return new overloaded(*this);
}
}
// Returns the signature-less type of the set.
ty *signatureless();
// True if one of the subtypes is castable.
bool castable(ty *target, caster &c);
// This is used to encapsulate iteration over the subtypes of an overloaded
// type. The base method need only be implemented to handle non-overloaded
// types.
class collector {
public:
virtual ~collector() {}
virtual ty *base(ty *target, ty *source) = 0;
virtual ty *collect(ty *target, ty *source) {
if (overloaded *o=dynamic_cast<overloaded *>(target)) {
ty_vector &sub=o->sub;
overloaded *oo=new overloaded;
for(ty_vector::iterator x = sub.begin(); x != sub.end(); ++x) {
types::ty *t=collect(*x, source);
if (t)
oo->add(t);
}
overloaded *oo=new overloaded;
for(ty_vector::iterator y = sub.begin(); y != sub.end(); ++y) {
// NOTE: A possible speed optimization would be to replace this with a
// call to base(), but this is only correct if we can guarantee that an
// overloaded type has no overloaded sub-types.
types::ty *t=collect(target, *y);
if (t)
oo->add(t);
}