bool TypeAnalyzer::isException(Type* type) { Type* exception = MakeType(TYPE_CLASS); exception->typedata._class.classname = strdup("Exception"); bool ret = isASubtypeOfB(type, exception); freeType(exception); return ret; }
bool TypeAnalyzer::isException(PureType<wake::QUALIFIED>* type) { PureType<wake::QUALIFIED> exception(TYPE_CLASS); exception.typedata._class.modulename = strdup("lang"); exception.typedata._class.classname = strdup("Exception"); bool ret = isASubtypeOfB(type, &exception); return ret; }
bool TypeAnalyzer::isASubtypeOfB(string a, string b) { try { if(a == b) return true; ReadOnlyPropertySymbolTable* a_data = reference->find(a); for(map<string, bool>::const_iterator it = a_data->getParentage().begin(); it != a_data->getParentage().end(); ++it) { if(isASubtypeOfB(it->first, b)) return true; } } catch(SymbolNotFoundException* e) { delete e; } return false; }
bool TypeAnalyzer::isASubtypeOfB(string a, string b) { try { if(a == b) return true; ReadOnlyPropertySymbolTable* a_data = reference->findFullyQualified(a); for(map<string, bool>::const_iterator it = a_data->getParentage().begin(); it != a_data->getParentage().end(); ++it) { PropertySymbolTable* a_child_data = reference->findFullyQualifiedModifiable(it->first); if(isASubtypeOfB(a_child_data->getAsPureType()->getFQClassname(), b)) return true; } } catch(SymbolNotFoundException* e) { delete e; } return false; }
bool TypeAnalyzer::isASubtypeOfB(PureType<wake::QUALIFIED>* a, PureType<wake::QUALIFIED>* b) { //if(a == NULL || b == NULL) return false; if(a->type == TYPE_MATCHALL || b->type == TYPE_MATCHALL) return true; if(a->type != b->type) { if(b->type == TYPE_OPTIONAL) { return isASubtypeOfB(a, b->typedata.optional.contained); } // TODO: parameterized types need logic here return false; } if(a->type == TYPE_LAMBDA) { // if one or the other is a null pointer, or has no args, and the other one has arguments. if((a->typedata.lambda.arguments == NULL || !a->typedata.lambda.arguments->typecount) != (b->typedata.lambda.arguments == NULL || !b->typedata.lambda.arguments->typecount)) return false; // Bool -- fn() is a subtype of void -- fn(), since the subtype will simply ignore the returnval // however, void --fn() is not a subtype of Bool -- fn() as you probably guessed if(a->typedata.lambda.returntype == NULL && b->typedata.lambda.returntype != NULL) return false; else if(b->typedata.lambda.returntype != NULL && !isASubtypeOfB(a->typedata.lambda.returntype, b->typedata.lambda.returntype)) // ChildClass -- fn() is a subtype of ParentClass -- fn() return false; if(a->typedata.lambda.arguments != NULL && a->typedata.lambda.arguments->typecount) { // A fn taking 3 arguments is not a subtype of a fn taking 2 or 4 if(a->typedata.lambda.arguments->typecount != b->typedata.lambda.arguments->typecount) return false; int i; for(i = 0; i < a->typedata.lambda.arguments->typecount; i++) if(!isASubtypeOfB(b->typedata.lambda.arguments->types[i], a->typedata.lambda.arguments->types[i])) // fn(ParentClass) is a subtype of fn(ChildClass), but not vice versa! (contravariance) return false; } return true; } else if(a->type == TYPE_CLASS) { // check if one pointer exists and the other is null: !ptr == 0 and !NULL == 1 if(!a->typedata._class.parameters != !b->typedata._class.parameters) { return false; } if(a->typedata._class.parameters) { // Here if A is not null, neither is B int len = a->typedata._class.parameters->typecount; if(b->typedata._class.parameters->typecount != len) { return false; } for(int i = 0; i < len; i++) if(!isAExactlyB(a->typedata._class.parameters->types[i], b->typedata._class.parameters->types[i])) return false; } if(a->getFQClassname() == b->getFQClassname()) { return true; } try { if(isASubtypeOfB(a->getFQClassname(), b->getFQClassname())) return true; } catch(SymbolNotFoundException* e) { delete e; } // special case... if(isPrimitiveTypeInt(a) && isPrimitiveTypeNum(b)) { return true; } return false; } else if(a->type == TYPE_PARAMETERIZED || a->type == TYPE_PARAMETERIZED_ARG) { if(a->typedata.parameterized.label == string(b->typedata.parameterized.label)) return true; // TODO: lower/upper bounds comparison return false; } else if(a->type == TYPE_LIST) { return isAExactlyB(a->typedata.list.contained, b->typedata.list.contained); } else if(a->type == TYPE_OPTIONAL) { return isASubtypeOfB(a->typedata.optional.contained, b->typedata.optional.contained); } }
boost::optional<PureType<wake::QUALIFIED>*> TypeAnalyzer::getCommonSubtypeOf(PureType<wake::QUALIFIED>* a, PureType<wake::QUALIFIED>* b) { if(a->type == TYPE_UNUSABLE || b->type == TYPE_UNUSABLE) return boost::optional<PureType<wake::QUALIFIED>*>(); // this maybe should return TYPE_UNUSABLE...but unusable types aren't necessarily similar at all if(a->type == TYPE_MATCHALL) return boost::optional<PureType<wake::QUALIFIED>*>(new PureType<wake::QUALIFIED>(*b)); if(b->type == TYPE_MATCHALL) return boost::optional<PureType<wake::QUALIFIED>*>(new PureType<wake::QUALIFIED>(*a)); if(a->type == TYPE_OPTIONAL || b->type == TYPE_OPTIONAL) { PureType<wake::QUALIFIED>* optional = a->type == TYPE_OPTIONAL ? a : b; PureType<wake::QUALIFIED>* other = a == optional ? b : a; if(other->type == TYPE_OPTIONAL) other = other->typedata.optional.contained; // [nothing, Text?] is common to type Text? // [X, Y?] is common to type Z? where Z is the common type to [X, Y] boost::optional<PureType<wake::QUALIFIED>*> nonoptcommon = getCommonSubtypeOf(optional->typedata.optional.contained, other); if(!nonoptcommon) { return nonoptcommon; } PureType<wake::QUALIFIED>* common = new PureType<wake::QUALIFIED>(TYPE_OPTIONAL); // [Text?, Text] becomes Text? common->typedata.optional.contained = *nonoptcommon; return boost::optional<PureType<wake::QUALIFIED>*>(common); } if(a->type != b->type) return boost::optional<PureType<wake::QUALIFIED>*>(); if(a->type == TYPE_LIST) { // [Text[], Num[]] and [Printer[], DisabledPrinter[]] are both common to nothing if(!isAExactlyB(a, b)) return boost::optional<PureType<wake::QUALIFIED>*>(); // but [Text[], Text[]] is common to Text[] return boost::optional<PureType<wake::QUALIFIED>*>(new PureType<wake::QUALIFIED>(*a)); } else if(a->type == TYPE_CLASS) { // check if one pointer exists and the other is null: !ptr == 0 and !NULL == 1 if(!a->typedata._class.parameters != !b->typedata._class.parameters) { return boost::optional<PureType<wake::QUALIFIED>*>(); } if(a->typedata._class.parameters) { // Here if A is not null, neither is B int len = a->typedata._class.parameters->typecount; if(b->typedata._class.parameters->typecount != len) { return boost::optional<PureType<wake::QUALIFIED>*>(); } for(int i = 0; i < len; i++) if(!isAExactlyB(a->typedata._class.parameters->types[i], b->typedata._class.parameters->types[i])) return boost::optional<PureType<wake::QUALIFIED>*>(); } if(isASubtypeOfB(a, b)) return boost::optional<PureType<wake::QUALIFIED>*>(new PureType<wake::QUALIFIED>(*b)); if(isASubtypeOfB(b, a)) return boost::optional<PureType<wake::QUALIFIED>*>(new PureType<wake::QUALIFIED>(*a)); boost::optional<pair<int, string> > common = getCommonFQClassnamesWithDepth(a->getFQClassname(), b->getFQClassname(), 0); if(!common || common->second == "") { return boost::optional<PureType<wake::QUALIFIED>*>(); } else { PureType<wake::QUALIFIED>* classType = new PureType<wake::QUALIFIED>(TYPE_CLASS); classType->typedata._class.classname = strdup(common->second.c_str()); return boost::optional<PureType<wake::QUALIFIED>*>(classType); } } else if(a->type == TYPE_PARAMETERIZED || a->type == TYPE_PARAMETERIZED_ARG) { if(a->typedata.parameterized.label == string(b->typedata.parameterized.label)) return boost::optional<PureType<wake::QUALIFIED>*>(new PureType<wake::QUALIFIED>(*a)); // TODO: lower/upper bounds comparison return boost::optional<PureType<wake::QUALIFIED>*>(); } if(a->type == TYPE_LAMBDA) { // if one or the other is a null, and the other one has arguments if((a->typedata.lambda.arguments == NULL || !a->typedata.lambda.arguments->typecount) != (b->typedata.lambda.arguments == NULL || !b->typedata.lambda.arguments->typecount)) return boost::optional<PureType<wake::QUALIFIED>*>(); if(a->typedata.lambda.arguments != NULL && a->typedata.lambda.arguments->typecount) { // A fn taking 3 arguments is not a subtype of a fn taking 2 or 4 if(a->typedata.lambda.arguments->typecount != b->typedata.lambda.arguments->typecount) return boost::optional<PureType<wake::QUALIFIED>*>(); int i; for(i = 0; i < a->typedata.lambda.arguments->typecount; i++) if(!isAExactlyB(a->typedata.lambda.arguments->types[i], b->typedata.lambda.arguments->types[i])) return boost::optional<PureType<wake::QUALIFIED>*>(); } // Bool -- fn() is a subtype of void -- fn(), since the subtype will simply ignore the returnval // however, void --fn() is not a subtype of Bool -- fn() as you probably guessed if(a->typedata.lambda.returntype == NULL || b->typedata.lambda.returntype == NULL) { PureType<wake::QUALIFIED>* newlambda = new PureType<wake::QUALIFIED>(*a); if(newlambda->typedata.lambda.returntype) { delete newlambda->typedata.lambda.returntype; } newlambda->typedata.lambda.returntype = NULL; return boost::optional<PureType<wake::QUALIFIED>*>(newlambda); } boost::optional<PureType<wake::QUALIFIED>*> commonReturn = getCommonSubtypeOf(a->typedata.lambda.returntype, b->typedata.lambda.returntype); if(!commonReturn) { PureType<wake::QUALIFIED>* newlambda = new PureType<wake::QUALIFIED>(*a); delete newlambda->typedata.lambda.returntype; newlambda->typedata.lambda.returntype = NULL; return boost::optional<PureType<wake::QUALIFIED>*>(newlambda); } PureType<wake::QUALIFIED>* newlambda = new PureType<wake::QUALIFIED>(*a); delete newlambda->typedata.lambda.returntype; newlambda->typedata.lambda.returntype = *commonReturn;; return boost::optional<PureType<wake::QUALIFIED>*>(newlambda); } }
bool TypeAnalyzer::isASubtypeOfB(Type* a, Type* b) { //if(a == NULL || b == NULL) return false; if(a->type == TYPE_MATCHALL || b->type == TYPE_MATCHALL) return true; if(a->type == TYPE_NOTHING) return b->optional; if(a->arrayed != b->arrayed) return false; if(a->type != b->type) return false; // TODO: parameterized types don't always follow this logic! if(a->type == TYPE_LAMBDA) { // if one or the other is a pointer if((a->typedata.lambda.arguments == NULL) != (b->typedata.lambda.arguments == NULL)) return false; // Bool -- fn() is a subtype of void -- fn(), since the subtype will simply ignore the returnval // however, void --fn() is not a subtype of Bool -- fn() as you probably guessed if(a->typedata.lambda.returntype == NULL && b->typedata.lambda.returntype != NULL) return false; else if(b->typedata.lambda.returntype != NULL && !isASubtypeOfB(a->typedata.lambda.returntype, b->typedata.lambda.returntype)) return false; if(a->typedata.lambda.arguments != NULL) { // A fn taking 3 arguments is not a subtype of a fn taking 2 or 4 if(a->typedata.lambda.arguments->typecount != b->typedata.lambda.arguments->typecount) return false; int i; for(i = 0; i < a->typedata.lambda.arguments->typecount; i++) if(!isASubtypeOfB(a->typedata.lambda.arguments->types[i], b->typedata.lambda.arguments->types[i])) return false; } return true; } else if(a->type == TYPE_CLASS) { // check if one pointer exists and the other is null: !ptr == 0 and !NULL == 1 if(!a->typedata._class.parameters != !b->typedata._class.parameters) { return false; } if(a->typedata._class.parameters) { // Here if A is not null, neither is B int len = a->typedata._class.parameters->typecount; if(b->typedata._class.parameters->typecount != len) { return false; } for(int i = 0; i < len; i++) if(!isAExactlyB(a->typedata._class.parameters->types[i], b->typedata._class.parameters->types[i])) return false; } if(string(a->typedata._class.classname) == b->typedata._class.classname) { return a->optional <= b->optional; } try { ReadOnlyPropertySymbolTable* a_data = reference->find(a->typedata._class.classname); for(map<string, bool>::const_iterator it = a_data->getParentage().begin(); it != a_data->getParentage().end(); ++it) { if(isASubtypeOfB(it->first, b->typedata._class.classname)) return true; } } catch(SymbolNotFoundException* e) { delete e; } return false; } else if(a->type == TYPE_PARAMETERIZED) { if(a->typedata.parameterized.label == string(b->typedata.parameterized.label)) return true; // TODO: lower/upper bounds comparison return false; } }