Template function instantiation type inference work - super simple inference works (i.e. id<T>(i:T) { return i; }), and the framework is good, just have to flesh out unifyType to handle more than the trivial case

This commit is contained in:
Nathan Braswell
2015-05-14 00:25:18 -04:00
parent d70fa4ebbb
commit f5e74ca7ce
5 changed files with 125 additions and 48 deletions

View File

@@ -47,7 +47,7 @@ class ASTTransformation: public NodeTransformation<Symbol,ASTData> {
NodeTree<ASTData>* doFunction(NodeTree<ASTData>* scope, std::string lookup, std::vector<NodeTree<ASTData>*> nodes, std::map<std::string, Type*> templateTypeReplacements);
NodeTree<ASTData>* functionLookup(NodeTree<ASTData>* scope, std::string lookup, std::vector<Type> types);
NodeTree<ASTData>* templateFunctionLookup(NodeTree<ASTData>* scope, std::string lookup, std::vector<Type*> templateInstantiationTypes, std::vector<Type> types);
NodeTree<ASTData>* templateFunctionLookup(NodeTree<ASTData>* scope, std::string lookup, std::vector<Type*>* templateInstantiationTypes, std::vector<Type> types);
std::vector<NodeTree<ASTData>*> scopeLookup(NodeTree<ASTData>* scope, std::string lookup, bool includeModules = false);
std::vector<NodeTree<ASTData>*> scopeLookup(NodeTree<ASTData>* scope, std::string lookup, bool includeModules, std::vector<NodeTree<ASTData>*> visited);
@@ -55,6 +55,8 @@ class ASTTransformation: public NodeTransformation<Symbol,ASTData> {
NodeTree<ASTData>* addToScope(std::string name, NodeTree<ASTData>* toAdd, NodeTree<ASTData>* addTo);
Type* typeFromTypeNode(NodeTree<Symbol>* typeNode, NodeTree<ASTData>* scope, std::map<std::string, Type*> templateTypeReplacements);
NodeTree<ASTData>* templateClassLookup(NodeTree<ASTData>* scope, std::string name, std::vector<Type*> templateInstantiationTypes);
void unifyType(NodeTree<Symbol> *syntaxType, Type type, std::map<std::string, Type>* templateTypeMap);
void unifyTemplateFunction(NodeTree<ASTData>* templateFunction, std::vector<Type> types, std::vector<Type*>* templateInstantiationTypes);
NodeTree<ASTData>* findOrInstantiateFunctionTemplate(std::vector<NodeTree<Symbol>*> children, NodeTree<ASTData>* scope, std::vector<Type> types, std::map<std::string, Type*> templateTypeReplacements);
std::map<std::string, Type*> makeTemplateFunctionTypeMap(NodeTree<Symbol>* templateNode, std::vector<Type*> types);
std::vector<std::pair<std::string, std::set<std::string>>> makeTemplateNameTraitPairs(NodeTree<Symbol>* templateNode);

View File

@@ -375,8 +375,10 @@ NodeTree<ASTData>* ASTTransformation::transform(NodeTree<Symbol>* from, NodeTree
if (types.size()) {
newNode = functionLookup(scope, lookupName, types);
if (newNode == NULL) {
std::cerr << "scope lookup error! Could not find " << lookupName << " in identifier (functionLookup)" << std::endl;
throw "LOOKUP ERROR: " + lookupName;
std::cout << "scope lookup failed! Could not find " << lookupName << " in identifier (functionLookup)" << std::endl;
std::cout << "(maybe this is supposted to happen because the function is a template and we're infrencing)" << std::endl;
//throw "LOOKUP ERROR: " + lookupName;
return nullptr;
}
} else {
auto possibleMatches = scopeLookup(scope, lookupName);
@@ -520,8 +522,7 @@ NodeTree<ASTData>* ASTTransformation::transform(NodeTree<Symbol>* from, NodeTree
if (name == "access_operation") {
std::cout << "lhs is: " << lhs->getDataRef()->toString() << std::endl;
rhs = transform(children[2], lhs->getDataRef()->valueType->typeDefinition, types, templateTypeReplacements); //If an access operation, then the right side will be in the lhs's type's scope
}
else
} else
rhs = transform(children[2], scope, types, templateTypeReplacements);
std::string functionCallName = concatSymbolTree(children[1]);
@@ -536,12 +537,17 @@ NodeTree<ASTData>* ASTTransformation::transform(NodeTree<Symbol>* from, NodeTree
}
return newNode;
//skipChildren.insert(1);
} else if (children.size() == 2) {
//Is template instantiation
return findOrInstantiateFunctionTemplate(children, scope, types, templateTypeReplacements);
} else {
return transform(children[0], scope, types, templateTypeReplacements); //Just a promoted child, so do it instead
}
if (children.size() == 1) {
newNode = transform(children[0], scope, types, templateTypeReplacements); //Just a promoted child, so do it instead
if (newNode)
return newNode;
}
// So if children.size() != 1, or that returned null because the function lookup failed,
// we try to do a template instatiation. If it had 2 children, it's an instantion, if it has 1
// maybe it's a template instantiation we're supposed to infer the types for. Either way, we let
// findorinstantiatefunctiontemplate take care of it.
return findOrInstantiateFunctionTemplate(children, scope, types, templateTypeReplacements);
} else if (name == "factor") { //Do factor here, as it has all the weird unary operators
//If this is an actual part of an expression, not just a premoted child
//NO SUPPORT FOR CASTING YET
@@ -917,11 +923,10 @@ NodeTree<ASTData>* ASTTransformation::functionLookup(NodeTree<ASTData>* scope, s
for (int j = 0; j < types.size(); j++) {
Type* tmpType = children[j]->getDataRef()->valueType;
//Don't worry if types don't match if it's a template type
// std::cout << "Checking for segfaults, we have" << std::endl;
// std::cout << types[j].toString() << std::endl;
// std::cout << tmpType->toString() << std::endl;
// std::cout << "Done!" << std::endl;
if (types[j] != *tmpType && tmpType->baseType != template_type_type) {
//if (types[j] != *tmpType && tmpType->baseType != template_type_type) {
// WE DO WORRY NOW B/C template type infrence is ugly and we need this to fail
// for regular function lookups so that we know to retry with a template
if (types[j] != *tmpType) {
typesMatch = false;
std::cout << "Types do not match between two " << lookup << " " << types[j].toString();
std::cout << " vs " << children[j]->getDataRef()->valueType->toString() << std::endl;
@@ -1005,8 +1010,41 @@ NodeTree<ASTData>* ASTTransformation::templateClassLookup(NodeTree<ASTData>* sco
return *mostFittingTemplates.begin();
}
void ASTTransformation::unifyType(NodeTree<Symbol> *syntaxType, Type type, std::map<std::string, Type>* templateTypeMap) {
// Ok, 3 options for syntaxType here.
// 1) This a basic type. (int, or object, etc)
// then check to see if it's the same as our type
// 2) This is a template type type (i.e. T)
// match! set up templateTypeMap[T] -> type
// 3) This some sort of instantiated template
// a) instantiated with some other type (i.e. vector<int>)
// this will be a bit of a pain
// b) instantiated with a template type type (i.e. vector<T>)
// this will be a bit of a pain too
auto children = syntaxType->getChildren();
if (children.size() == 1) {
(*templateTypeMap)[concatSymbolTree(children.back())] = type;
} else {
throw "the inference just isn't good enough";
}
}
void ASTTransformation::unifyTemplateFunction(NodeTree<ASTData>* templateFunction, std::vector<Type> types, std::vector<Type*>* templateInstantiationTypes) {
NodeTree<Symbol>* templateSyntaxTree = templateFunction->getDataRef()->valueType->templateDefinition;
std::vector<NodeTree<Symbol>*> templateParameters = getNodes("typed_parameter", templateSyntaxTree);
if (templateParameters.size() != types.size())
return;
std::map<std::string, Type> templateTypeMap;
for (int i = 0; i < types.size(); i++)
unifyType(getNode("type", templateParameters[i]), types[i], &templateTypeMap);
for (auto instantiationParam : getNodes("template_param", getNode("template_dec", templateSyntaxTree)))
templateInstantiationTypes->push_back(templateTypeMap[concatSymbolTree(instantiationParam)].clone());
}
//Lookup function for template functions. It has some extra concerns compared to function lookup, namely traits
NodeTree<ASTData>* ASTTransformation::templateFunctionLookup(NodeTree<ASTData>* scope, std::string lookup, std::vector<Type*> templateInstantiationTypes, std::vector<Type> types) {
NodeTree<ASTData>* ASTTransformation::templateFunctionLookup(NodeTree<ASTData>* scope, std::string lookup, std::vector<Type*>* templateInstantiationTypes, std::vector<Type> types) {
std::map<NodeTree<ASTData>*, std::vector<Type*>> templateInstantiationTypesPerFunction;
std::set<NodeTree<ASTData>*> mostFittingTemplates;
int bestNumTraitsSatisfied = -1;
auto possibleMatches = scopeLookup(scope, lookup);
@@ -1019,10 +1057,14 @@ NodeTree<ASTData>* ASTTransformation::templateFunctionLookup(NodeTree<ASTData>*
std::cout << "Not a template, skipping" << std::endl;
continue;
}
// If template instantiation was explicit, use those types. Otherwise, unify to find them
if (templateInstantiationTypes->size())
templateInstantiationTypesPerFunction[i] = *templateInstantiationTypes;
else
unifyTemplateFunction(i, types, &templateInstantiationTypesPerFunction[i]);
auto nameTraitsPairs = makeTemplateNameTraitPairs(templateSyntaxTree->getChildren()[1]);
//Check if sizes match between the placeholder and actual template types
if (nameTraitsPairs.size() != templateInstantiationTypes.size())
if (nameTraitsPairs.size() != templateInstantiationTypesPerFunction[i].size())
continue;
std::map<std::string, Type*> typeMap;
@@ -1030,23 +1072,23 @@ NodeTree<ASTData>* ASTTransformation::templateFunctionLookup(NodeTree<ASTData>*
int typeIndex = 0;
int currentTraitsSatisfied = 0;
for (auto j : nameTraitsPairs) {
if (!subset(j.second, templateInstantiationTypes[typeIndex]->traits)) {
if (!subset(j.second, templateInstantiationTypesPerFunction[i][typeIndex]->traits)) {
traitsEqual = false;
std::cout << "Traits not a subset for " << j.first << " and " << templateInstantiationTypes[typeIndex]->toString() << ": ";
std::cout << "Traits not a subset for " << j.first << " and " << templateInstantiationTypesPerFunction[i][typeIndex]->toString() << ": ";
std::copy(j.second.begin(), j.second.end(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << " vs ";
std::copy(templateInstantiationTypes[typeIndex]->traits.begin(), templateInstantiationTypes[typeIndex]->traits.end(), std::ostream_iterator<std::string>(std::cout, " "));
std::copy(templateInstantiationTypesPerFunction[i][typeIndex]->traits.begin(), templateInstantiationTypesPerFunction[i][typeIndex]->traits.end(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << std::endl;
break;
} else {
std::cout << "Traits ARE a subset for " << j.first << " and " << templateInstantiationTypes[typeIndex]->toString() << ": ";
std::cout << "Traits ARE a subset for " << j.first << " and " << templateInstantiationTypesPerFunction[i][typeIndex]->toString() << ": ";
std::copy(j.second.begin(), j.second.end(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << " vs ";
std::copy(templateInstantiationTypes[typeIndex]->traits.begin(), templateInstantiationTypes[typeIndex]->traits.end(), std::ostream_iterator<std::string>(std::cout, " "));
std::copy(templateInstantiationTypesPerFunction[i][typeIndex]->traits.begin(), templateInstantiationTypesPerFunction[i][typeIndex]->traits.end(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << std::endl;
}
//As we go, build up the typeMap for when we transform the parameters for parameter checking
typeMap[j.first] = templateInstantiationTypes[typeIndex];
typeMap[j.first] = templateInstantiationTypesPerFunction[i][typeIndex];
currentTraitsSatisfied += j.second.size();
typeIndex++;
}
@@ -1087,6 +1129,11 @@ NodeTree<ASTData>* ASTTransformation::templateFunctionLookup(NodeTree<ASTData>*
std::cerr << "Multiple template functions fit with equal number of traits satisfied for " << lookup << "!" << std::endl;
throw "Multiple matching template functions";
}
// Assign our most fitting instantiation types to what we were passed in
// if it was empty
if (templateInstantiationTypes->size() == 0)
*templateInstantiationTypes = templateInstantiationTypesPerFunction[*mostFittingTemplates.begin()];
std::cout << *mostFittingTemplates.begin() << std::endl;
return *mostFittingTemplates.begin();
}
@@ -1263,7 +1310,7 @@ Type* ASTTransformation::typeFromTypeNode(NodeTree<Symbol>* typeNode, NodeTree<A
* WE RETURN EARLY IF ONE OF OUR REPLACEMENT INST TYPES IS TEMPLATE_TYPE_TYPE
* WE DO THIS BECAUSE WE CAN'T ACTUALLY INSTATNTIATE WITH THIS.
* THIS CAN HAPPEN IN THE FOLLOWING SITUATIONS.
* template<T> |T| fun(|vec<T>| a) { return a.at(0); }
* fun example<T>(a:vec<T>):T { return a.at(0); }
* etc
*******************************************************************************/
if (instType->baseType == template_type_type)
@@ -1351,24 +1398,34 @@ NodeTree<ASTData>* ASTTransformation::findOrInstantiateFunctionTemplate(std::vec
//First look to see if we can find this already instantiated
std::cout << "\n\nFinding or instantiating templated function\n\n" << std::endl;
std::string functionName = concatSymbolTree(children[0]);
auto unsliced = children[1]->getChildren();
std::vector<NodeTree<Symbol>*> templateParamInstantiationNodes = slice(unsliced, 1 , -2, 2);//skip <, >, and commas
std::string instTypeString = "";
std::string fullyInstantiatedName;
std::vector<Type*> templateActualTypes;
for (int i = 0; i < templateParamInstantiationNodes.size(); i++) {
Type* instType = typeFromTypeNode(templateParamInstantiationNodes[i],scope, templateTypeReplacements);
instTypeString += (instTypeString == "" ? instType->toString() : "," + instType->toString());
templateActualTypes.push_back(instType);
}
std::cout << "Size: " << templateParamInstantiationNodes.size() << std::endl;
std::string fullyInstantiatedName = functionName + "<" + instTypeString + ">";
std::cout << "Looking for " << fullyInstantiatedName << std::endl;
NodeTree<ASTData>* templateDefinition = NULL;
// Are we supposed to infer our instantiation, or not? If we have only one child we're inferring as we don't
// have the actual instantiation part. If do have the instantiation part, then we'll use that.
// Note that as a part o finferring the instantiation we already find the template, so we make that
// condtitional too (templateDefinition)
if (children.size() == 1) {
// templateFunctionLookup adds the actual types to templateActualTypes if it's currently empty
templateDefinition = templateFunctionLookup(scope, functionName, &templateActualTypes, types);
} else {
auto unsliced = children[1]->getChildren();
std::vector<NodeTree<Symbol>*> templateParamInstantiationNodes = slice(unsliced, 1 , -2, 2);//skip <, >, and commas
std::string instTypeString = "";
for (int i = 0; i < templateParamInstantiationNodes.size(); i++) {
Type* instType = typeFromTypeNode(templateParamInstantiationNodes[i],scope, templateTypeReplacements);
instTypeString += (instTypeString == "" ? instType->toString() : "," + instType->toString());
templateActualTypes.push_back(instType);
}
std::cout << "Size: " << templateParamInstantiationNodes.size() << std::endl;
fullyInstantiatedName = functionName + "<" + instTypeString + ">";
std::cout << "Looking for " << fullyInstantiatedName << std::endl;
}
std::cout << "Types are : ";
for (auto i : types)
std::cout << " " << i.toString();
std::cout << std::endl;
for (auto i : types)
std::cout << " " << i.toString();
std::cout << std::endl;
NodeTree<ASTData>* instantiatedFunction = functionLookup(scope, fullyInstantiatedName, types);
//If it already exists, return it
@@ -1386,7 +1443,11 @@ NodeTree<ASTData>* ASTTransformation::findOrInstantiateFunctionTemplate(std::vec
//Otherwise, we're going to instantiate it
//Find the template definitions
NodeTree<ASTData>* templateDefinition = templateFunctionLookup(scope, functionName, templateActualTypes, types);
// templateFunctionLookup adds the actual types to templateActualTypes if it's currently empty
// by here, it's not as either we had the instantiation already or we figured out out before
// and are not actually doing this call
if (!templateDefinition)
templateDefinition = templateFunctionLookup(scope, functionName, &templateActualTypes, types);
if (templateDefinition == NULL) {
std::cout << functionName << " search turned up null, returing null" << std::endl;
return NULL;
@@ -1402,17 +1463,12 @@ NodeTree<ASTData>* ASTTransformation::findOrInstantiateFunctionTemplate(std::vec
std::cout << std::endl;
instantiatedFunction = new NodeTree<ASTData>("function", ASTData(function, Symbol(fullyInstantiatedName, true), typeFromTypeNode(templateChildren[templateChildren.size()-2], scope, newTemplateTypeReplacement)));
//scope->getDataRef()->scope[fullyInstantiatedName].push_back(instantiatedFunction);
//instantiatedFunction->getDataRef()->scope["~enclosing_scope"].push_back(templateDefinition->getDataRef()->scope["~enclosing_scope"][0]); //Instantiated Template Function's scope is it's template's definition's scope
addToScope("~enclosing_scope", templateDefinition->getDataRef()->scope["~enclosing_scope"][0], instantiatedFunction);
// Arrrrrgh this has a hard time working because the functions will need to see their parameter once they are emitted as C.
// HAHAHAHAHA DOESN'T MATTER ALL ONE C FILE NOW, swap back to old way
auto templateTopScope = getUpperTranslationUnit(templateDefinition);
//templateTopScope->getDataRef()->scope[fullyInstantiatedName].push_back(instantiatedFunction);
addToScope(fullyInstantiatedName, instantiatedFunction, templateTopScope);
templateTopScope->addChild(instantiatedFunction); // Add this object the the highest scope's
//topScope->getDataRef()->scope[fullyInstantiatedName].push_back(instantiatedFunction);
//topScope->addChild(instantiatedFunction); //Add this object the the highest scope's
std::set<int> skipChildren;
skipChildren.insert(0);

View File

@@ -0,0 +1,2 @@
11
12

View File

@@ -0,0 +1,17 @@
import io:*
import mem:*
import vector:*
fun id<T>(in: T): T { return in; }
fun idVec<T>(in: vector<T>): T { return in.get(0); }
fun main():int {
var fromTemplateFun = id(11)
var aVec.construct(): vector<int>
aVec.addEnd(12)
//var fromTemplateFun = id<int>(11);
println(fromTemplateFun)
println(idVec(aVec))
//println(idVec<int>(aVec))
return 0
}

View File

@@ -28,14 +28,14 @@ fun main():int {
var add = 9 + 3
var flt = 9.3
var msg = retMessage()
var fromTemplateFun = id<int>(11);
var fromTemplateFun = id(11);
var vec = new<vector<int>>()->construct()
vec->addEnd(avar)
var src: CustomObj
src.data = 80
var dst = src
var throughTemp = inFun<char*>("complicated");
var throughTemp = inFun("complicated");
println(str)
println(avar)