FunctionCallPtr Interpreter::newFunctionCall (const std::string &functionName) { Lock lock (_data->mutex); // // Calling a CTL function with variable-size array arguments // from C++ is not supported. // const SymbolInfoPtr info = symtab().lookupSymbol (functionName); if (!info) THROW (ArgExc, "Cannot find CTL function " << functionName << "."); if (!info->isFunction()) THROW (TypeExc, "CTL object " << functionName << " is not a function " "(it is of type " << info->type()->asString() << ")."); const FunctionTypePtr fType = info->type(); const ParamVector ¶meters = fType->parameters(); for (int i = parameters.size() - 1; i >= 0; --i) { const Param ¶m = parameters[i]; ArrayTypePtr aType = param.type.cast<ArrayType>(); if(aType) { SizeVector sizes; aType->sizes (sizes); for (int j = 0; j < sizes.size(); j++) { if (sizes[j] == 0) THROW (ArgExc, "CTL function " << functionName << " " "has a variable-size array " "argument, " << param.name << ", and can " "only be called by another CTL function."); } } } return newFunctionCallInternal (info, functionName); }
TEST(TestFunc, testFunc_ReturnFunc) { PARSE_STATEMENT(L"func chooseStepFunction(backwards: Bool) -> (Int) -> Int {" L"return backwards ? stepBackward : stepForward" L"}"); FunctionDefPtr func; ParametersNodePtr params; ParameterNodePtr param; TypeIdentifierPtr type; FunctionTypePtr rettype; TupleTypePtr tupleType; ASSERT_NOT_NULL(func = std::dynamic_pointer_cast<FunctionDef>(root)); ASSERT_EQ(L"chooseStepFunction", func->getName()); ASSERT_EQ(1, func->numParameters()); ASSERT_NOT_NULL(params = func->getParameters(0)); ASSERT_EQ(1, params->numParameters()); ASSERT_FALSE(params->isVariadicParameters()); ASSERT_NOT_NULL(param = params->getParameter(0)); ASSERT_EQ(ParameterNode::None, param->getAccessibility()); ASSERT_FALSE(param->isShorthandExternalName()); ASSERT_FALSE(param->isInout()); ASSERT_NULL(param->getDefaultValue()); ASSERT_EQ(L"", param->getExternalName()); ASSERT_EQ(L"backwards", param->getLocalName()); ASSERT_NOT_NULL(type = std::dynamic_pointer_cast<TypeIdentifier>(param->getDeclaredType())); ASSERT_EQ(L"Bool", type->getName()); ASSERT_NOT_NULL(rettype = std::dynamic_pointer_cast<FunctionType>(func->getReturnType())); ASSERT_NOT_NULL(tupleType = std::dynamic_pointer_cast<TupleType>(rettype->getArgumentsType())); ASSERT_FALSE(tupleType->getVariadicParameters()); ASSERT_EQ(1, tupleType->numElements()); ASSERT_NOT_NULL(type = std::dynamic_pointer_cast<TypeIdentifier>(tupleType->getElementType(0))); ASSERT_EQ(L"Int", type->getName()); ASSERT_NOT_NULL(type = std::dynamic_pointer_cast<TypeIdentifier>(rettype->getReturnType())); ASSERT_EQ(L"Int", type->getName()); }
TypePtr tryDeduceReturnType(const FunctionTypePtr& funType, const TypeList& argumentTypes) { NodeManager& manager = funType->getNodeManager(); // try deducing variable instantiations the argument types auto varInstantiation = types::getTypeVariableInstantiation(manager, funType->getParameterTypes()->getTypes(), argumentTypes); // check whether derivation was successful if(!varInstantiation) { std::stringstream msg; msg << "Cannot match arguments (" << join(", ", argumentTypes, print<deref<TypePtr>>()) << ") \n" " to parameters (" << join(", ", funType->getParameterTypes(), print<deref<TypePtr>>()) << ")"; throw ReturnTypeDeductionException(msg.str()); } // extract return type const TypePtr& resType = funType->getReturnType(); // compute and return the expected return type return varInstantiation->applyTo(manager, resType); }
TypeVariableSet getTypeVariablesBoundBy(const FunctionTypePtr& funType) { // collect all free type variables int he parameters TypeVariableSet res; // collect all type variables for(const auto& paramType : funType->getParameterTypes()) { visitDepthFirstOnce(paramType, [&](const TypeVariablePtr& var) { res.insert(var); }); } // done return res; }
TypePtr deduceReturnType(const FunctionTypePtr& funType, const TypeList& argumentTypes, bool unitOnFail) { try { // try deducing the return type ... return tryDeduceReturnType(funType, argumentTypes); } catch (const ReturnTypeDeductionException&) { // didn't work => print a warning LOG(DEBUG) << "Unable to deduce return type for call to function of type " << toString(*funType) << " using arguments " << join(", ", argumentTypes, print<deref<TypePtr>>()); } // return null ptr return unitOnFail ? funType->getNodeManager().getLangBasic().getUnit() : TypePtr(); }
SimdFunctionCall::SimdFunctionCall (SimdInterpreter &interpreter, const string &name, FunctionTypePtr type, SimdInstAddrPtr addr, SymbolTable &symbols) : FunctionCall (name), _xcontext (interpreter), _entryPoint (addr->inst()), _symbols(symbols) { { SimdReg *returnReg = new SimdReg (type->returnVarying(), type->returnType()->alignedObjectSize()); _xcontext.stack().push (returnReg, TAKE_OWNERSHIP); setReturnValue (new SimdFunctionArg ("", this, type->returnType(), type->returnVarying(), returnReg)); } const ParamVector ¶meters = type->parameters(); vector<FunctionArgPtr> inputs; vector<FunctionArgPtr> outputs; for (int i = parameters.size() - 1; i >= 0; --i) { const Param ¶m = parameters[i]; SimdReg *paramReg = new SimdReg (param.varying, param.type->alignedObjectSize()); _xcontext.stack().push (paramReg, TAKE_OWNERSHIP); FunctionArgPtr arg = new SimdFunctionArg (param.name, this, param.type, param.varying, paramReg); if (param.isWritable()) outputs.push_back(arg); else inputs.push_back(arg); } int count = 0; for(vector<FunctionArgPtr>::reverse_iterator it = inputs.rbegin(); it != inputs.rend(); ++it) { setInputArg (count++, *it); } count = 0; for(vector<FunctionArgPtr>::reverse_iterator it = outputs.rbegin(); it != outputs.rend(); ++it) { setOutputArg (count++, *it); } }
TypeNodePtr Parser::parseType() { Token token; TypeNodePtr ret = NULL; expect_next(token); restore(token); if(token == TokenType::OpenParen) { ret = parseTupleType(); } else if(token.type == TokenType::Identifier && token.identifier.keyword == Keyword::_) { ret = parseTypeIdentifier(); } else if(token.type == TokenType::Identifier && token.identifier.keyword == Keyword::SelfType) { ret = parseTypeIdentifier(); } else if(token.getKeyword() == Keyword::Protocol) { ret = parseProtocolComposition(); } else if(token == TokenType::OpenBracket) { ret = parseCollectionType(); } else { unexpected(token); } do { if(!next(token)) break; //type chaining if(token == L"->") { //function-type → type->type TupleTypePtr argType = std::dynamic_pointer_cast<TupleType>(ret); if(!argType) { //wrap ret as a tuple type argType = nodeFactory->createTupleType(*ret->getSourceInfo()); argType->add(false, L"", ret); } TypeNodePtr retType = parseType(); FunctionTypePtr func = nodeFactory->createFunctionType(token.state); func->setArgumentsType(argType); func->setReturnType(retType); ret = func; continue; } if(token == TokenType::OpenBracket) { expect(L"]"); ArrayTypePtr array = nodeFactory->createArrayType(token.state); array->setInnerType(ret); ret = array; continue; } if(token == L"?") { //optional-type → type? OptionalTypePtr type = nodeFactory->createOptionalType(token.state); type->setInnerType(ret); ret = type; continue; } if(token == L"!") { //implicitly-unwrapped-optional-type → type! ImplicitlyUnwrappedOptionalPtr type = nodeFactory->createImplicitlyUnwrappedOptional(token.state); type->setInnerType(ret); ret = type; continue; } // metatype-type → type.Type type.Protocol //TODO meta type is not supported restore(token); break; }while(true); return ret; }
void CallNode::computeType (LContext &lcontext, const SymbolInfoPtr &initInfo) { // // Compute the type of a function call. This is the same type // as the function's return type, provided the call is valid. // // // Verify that what we are trying to call is actually a function. // if (!function) return; function->computeType (lcontext, initInfo); if (!function->type) return; FunctionTypePtr functionType = function->type.cast <FunctionType>(); if (!functionType) { MESSAGE_LE (lcontext, ERR_NON_FUNC, function->lineNumber, "Invalid function call to call non-function " "(" << function->name << " is of type " << function->type->asString() << ")."); return; } // // We shouldn't have more function call arguments than parameters. // const ParamVector ¶meters = functionType->parameters(); if (arguments.size() > parameters.size()) { MESSAGE_LE (lcontext, ERR_FUNC_ARG_NUM, function->lineNumber, "Too many arguments in call to function " << function->name << "."); return; } // // Compare the types of the arguments to the call with // the types of the function's parameters. // for (int i = 0; i < (int)parameters.size(); ++i) { if (i < (int)arguments.size()) { // // We have a function call argument for parameter i. // arguments[i]->computeType (lcontext, initInfo); TypePtr argType = arguments[i]->type; if (!argType) return; DataTypePtr paramType = parameters[i].type; if (parameters[i].isWritable()) { // // output parameter -- argument must be an lvalue // of the same type as the parameter // if (!arguments[i]->isLvalue (initInfo)) { MESSAGE_LE (lcontext, ERR_FUNC_ARG_LVAL, arguments[i]->lineNumber, "Argument " << i+1 << " in call to function " << function->name << " corresponds to an output " "parameter but it is not an lvalue."); return; } if (!paramType->isSameTypeAs (argType)) { MESSAGE_LE (lcontext, ERR_FUNC_ARG_TYPE, arguments[i]->lineNumber, "Type of argument " << i+1 << " in call to " "function " << function->name << " is not the " "same as the type of the function's parameter " "(" << argType->asString() << " value " "for " << paramType->asString() << " parameter.)"); return; } } else { // // input parameter -- it must be possible to cast // the argument type to the parameter type // if (!paramType->canCastFrom (argType)) { MESSAGE_LE (lcontext, ERR_FUNC_ARG_TYPE, arguments[i]->lineNumber, "Cannot convert the type of argument " << i+1 << " " "in call to function " << function->name << " " "to the type of the function's parameter " "(" << argType->asString() << " value " "for " << paramType->asString() << " parameter.)"); return; } } } else { // // We have no function call argument for parameter i. // The parameter must have a default value. // if (!parameters[i].defaultValue) { MESSAGE_LE (lcontext, ERR_FUNC_ARG_NUM, function->lineNumber, "Not enough arguments in call to " "function " << function->name << "."); return; } } } // // If we get here, then the call is valid. // type = functionType->returnType(); }
/** * Computes a join or meet type for the given pair of types. The join flag allows to determine * whether the join or meet type is computed. */ TypePtr getJoinMeetType(const TypePtr& typeA, const TypePtr& typeB, bool join) { static const TypePtr fail = 0; // add a structure based algorithm for computing the Join-Type // shortcut for equal types if (*typeA == *typeB) { return typeA; } // the rest depends on the node types NodeType nodeTypeA = typeA->getNodeType(); NodeType nodeTypeB = typeB->getNodeType(); // handle generic types if (nodeTypeA == NT_GenericType && nodeTypeB == NT_GenericType) { // let the join computation handle the case const GenericTypePtr& genTypeA = static_pointer_cast<const GenericType>(typeA); const GenericTypePtr& genTypeB = static_pointer_cast<const GenericType>(typeB); return (join) ? getJoinType(genTypeA, genTypeB) : getMeetType(genTypeA, genTypeB); } // handle vector types (only if array super type of A is a super type of B) // make sure typeA is the vector if (nodeTypeA != NT_VectorType && nodeTypeB == NT_VectorType) { // switch sides return getJoinMeetType(typeB, typeA, join); } // handle vector-array conversion (only works for joins) if (join && nodeTypeA == NT_VectorType) { VectorTypePtr vector = static_pointer_cast<const VectorType>(typeA); // the only potential super type is an array of the same element type IRBuilder builder(vector->getNodeManager()); ArrayTypePtr array = builder.arrayType(vector->getElementType()); if (isSubTypeOf(typeB, array)) { return array; } // no common super type! return fail; } // the rest can only work if it is of the same kind if (nodeTypeA != nodeTypeB) { // => no common super type return fail; } // check for functions if (nodeTypeA == NT_FunctionType) { FunctionTypePtr funTypeA = static_pointer_cast<const FunctionType>(typeA); FunctionTypePtr funTypeB = static_pointer_cast<const FunctionType>(typeB); // check number of arguments auto paramsA = funTypeA->getParameterTypes(); auto paramsB = funTypeB->getParameterTypes(); if (paramsA.size() != paramsB.size()) { // not matching return fail; } // check function kind FunctionKind resKind = funTypeA->getKind(); if (funTypeA->getKind() != funTypeB->getKind()) { // differences are only allowed when going from plain to closure type if ((funTypeA->isPlain() && funTypeB->isClosure()) || (funTypeA->isClosure() && funTypeB->isPlain())) { resKind = FK_CLOSURE; } else { return fail; } } // compute join type // JOIN/MEET result and argument types - if possible TypePtr cur = getJoinMeetType(funTypeA->getReturnType(), funTypeB->getReturnType(), join); TypePtr resType = cur; // continue with parameters TypeList params; for (std::size_t i=0; i<paramsA.size(); i++) { // ATTENTION: this goes in the reverse direction cur = getJoinMeetType(paramsA[i], paramsB[i], !join); // if a pair can not be matched => fail if (!cur) return fail; params.push_back(cur); } // construct resulting type IRBuilder builder(funTypeA->getNodeManager()); return builder.functionType(params, resType, resKind); } // everything else does not have a common join/meet type return fail; }
bool isSubTypeOf(const TypePtr& subType, const TypePtr& superType) { // quick check - reflexivity if (*subType == *superType) { return true; } // check for recursive types if (subType->getNodeType() == NT_RecType || superType->getNodeType() == NT_RecType) { // since they are not equivalent we have to compare the unrolled version of the sub with the super type if (subType->getNodeType() == NT_RecType) { return isSubTypeOf(subType.as<RecTypePtr>()->unroll(), superType); } if (superType->getNodeType() == NT_RecType) { assert(subType->getNodeType() != NT_RecType); return isSubTypeOf(subType, superType.as<RecTypePtr>()->unroll()); } assert(false && "How could you get here?"); } // check whether the sub-type is generic if (subType->getNodeType() == NT_GenericType || subType->getNodeType() == NT_StructType) { // use the delta algorithm for computing all the super-types of the given sub-type return isSubTypeOfInternal(subType, superType); } // check for vector types if (subType.isa<VectorTypePtr>() ) { VectorTypePtr vector = static_pointer_cast<const VectorType>(subType); // potential super type is an array of the same element type IRBuilder builder(vector->getNodeManager()); return *superType == *builder.arrayType(vector->getElementType()); } // for all other relations, the node type has to be the same if (subType->getNodeType() != superType->getNodeType()) { return false; } // check function types if (subType->getNodeType() == NT_FunctionType) { FunctionTypePtr funTypeA = static_pointer_cast<const FunctionType>(subType); FunctionTypePtr funTypeB = static_pointer_cast<const FunctionType>(superType); // check kind of functions if (funTypeA->getKind() != funTypeB->getKind()) { // only closure to plain conversion is allowed if (!(funTypeB->isClosure() && funTypeA->isPlain())) { return false; } } bool res = true; res = res && funTypeA->getParameterTypes().size() == funTypeB->getParameterTypes().size(); res = res && isSubTypeOf(funTypeA->getReturnType(), funTypeB->getReturnType()); for(std::size_t i = 0; res && i<funTypeB->getParameterTypes().size(); i++) { res = res && isSubTypeOf(funTypeB->getParameterTypes()[i], funTypeA->getParameterTypes()[i]); } return res; } // check reference types if (subType->getNodeType() == NT_RefType) { const auto& basic = subType->getNodeManager().getLangBasic(); // check source / sink auto srcRef = subType.as<RefTypePtr>(); auto trgRef = superType.as<RefTypePtr>(); // check read/write privileges if (trgRef.isRead() && !srcRef.isRead()) return false; if (trgRef.isWrite() && !srcRef.isWrite()) return false; // check element type auto srcElement = subType.as<RefTypePtr>()->getElementType(); auto trgElement = superType.as<RefTypePtr>()->getElementType(); // if element types are identical => it is fine //if (srcElement == trgElement) return true; if(core::analysis::compareTypes(srcElement, trgElement)) return true; // if sub-type is ref<any> it is ok if (basic.isAny(trgElement)) { return true; } // support nested references if (srcElement.isa<RefTypePtr>() && trgElement.isa<RefTypePtr>()) { return isSubTypeOf(srcElement, trgElement); } // a ref<vector<X,Y>> is a sub-type of a ref<array<X,1>> if (trgElement.isa<ArrayTypePtr>() && srcElement.isa<VectorTypePtr>()) { IRBuilder builder(srcElement.getNodeManager()); auto one = builder.concreteIntTypeParam(1); // array needs to be 1-dimensional and both have to have the same element type return trgElement.as<ArrayTypePtr>()->getDimension() == one && trgElement.as<ArrayTypePtr>()->getElementType() == srcElement.as<VectorTypePtr>()->getElementType(); } // also support references of derived classes being passed to base-type pointer if (core::analysis::isObjectType(srcElement) && core::analysis::isObjectType(trgElement)) { if (isSubTypeOf(srcElement, trgElement)) { return true; } } } // no other relations are supported return false; }