From e009c0a61d1c3b9f39fa4c9c4af181341332a452 Mon Sep 17 00:00:00 2001 From: Matt Pharr Date: Sun, 30 Oct 2011 14:12:12 -0700 Subject: [PATCH] Be able to determine if two types can be converted without requiring an Expr *. The Expr::TypeConv() method has been replaced with both a CanConvertTypes() routine that indicates whether one type can be converted to another and a TypeConvertExpr() routine that provides the same functionality as Expr::TypeConv() used to. --- ctx.cpp | 17 +-- expr.cpp | 339 ++++++++++++++++++++++++++++++----------------------- expr.h | 29 +++-- module.cpp | 4 +- stmt.cpp | 18 +-- 5 files changed, 231 insertions(+), 176 deletions(-) diff --git a/ctx.cpp b/ctx.cpp index 653c666c..1f2191a3 100644 --- a/ctx.cpp +++ b/ctx.cpp @@ -652,18 +652,19 @@ FunctionEmitContext::CurrentLanesReturned(Expr *expr, bool doCoherenceCheck) { } else { if (expr == NULL) { - Error(funcStartPos, - "Must provide return value for return statement for non-void function."); + Error(funcStartPos, "Must provide return value for return " + "statement for non-void function."); return; } - // Use a masked store to store the value of the expression in the - // return value memory; this preserves the return values from other - // lanes that may have executed return statements previously. - Expr *r = expr->TypeConv(returnType, "return statement"); - if (r != NULL) { - llvm::Value *retVal = r->GetValue(this); + expr = TypeConvertExpr(expr, returnType, "return statement"); + if (expr != NULL) { + llvm::Value *retVal = expr->GetValue(this); if (retVal != NULL) + // Use a masked store to store the value of the expression + // in the return value memory; this preserves the return + // values from other lanes that may have executed return + // statements previously. StoreInst(retVal, returnValuePtr, GetInternalMask(), returnType); } } diff --git a/expr.cpp b/expr.cpp index 2e1be3c5..ba8d825f 100644 --- a/expr.cpp +++ b/expr.cpp @@ -125,33 +125,34 @@ lMaybeIssuePrecisionWarning(const AtomicType *toAtomicType, } #endif -Expr * -Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk, - bool issuePrecisionWarnings) { +/////////////////////////////////////////////////////////////////////////// + +static bool +lDoTypeConv(const Type *fromType, const Type *toType, Expr **expr, + bool failureOk, const char *errorMsgBase, SourcePos pos) { /* This function is way too long and complex. Is type conversion stuff always this messy, or can this be cleaned up somehow? */ assert(failureOk || errorMsgBase != NULL); - const Type *fromType = GetType(); if (toType == NULL || fromType == NULL) - return this; + return false; // The types are equal; there's nothing to do if (Type::Equal(toType, fromType)) - return this; + return true; if (fromType == AtomicType::Void) { if (!failureOk) Error(pos, "Can't convert from \"void\" to \"%s\" for %s.", toType->GetString().c_str(), errorMsgBase); - return NULL; + return false; } if (toType == AtomicType::Void) { if (!failureOk) Error(pos, "Can't convert type \"%s\" to \"void\" for %s.", fromType->GetString().c_str(), errorMsgBase); - return NULL; + return false; } if (toType->IsUniformType() && fromType->IsVaryingType()) { @@ -159,13 +160,24 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk, Error(pos, "Can't convert from varying type \"%s\" to uniform " "type \"%s\" for %s.", fromType->GetString().c_str(), toType->GetString().c_str(), errorMsgBase); - return NULL; + return false; } + const ArrayType *toArrayType = dynamic_cast(toType); + const ArrayType *fromArrayType = dynamic_cast(fromType); + const VectorType *toVectorType = dynamic_cast(toType); + const VectorType *fromVectorType = dynamic_cast(fromType); + const StructType *toStructType = dynamic_cast(toType); + const StructType *fromStructType = dynamic_cast(fromType); + const EnumType *toEnumType = dynamic_cast(toType); + const EnumType *fromEnumType = dynamic_cast(fromType); + const AtomicType *toAtomicType = dynamic_cast(toType); + const AtomicType *fromAtomicType = dynamic_cast(fromType); + // Convert from type T -> const T; just return a TypeCast expr, which // can handle this if (Type::Equal(toType, fromType->GetAsConstType())) - return new TypeCastExpr(toType, this, false, pos); + goto typecast_ok; if (dynamic_cast(fromType)) { if (dynamic_cast(toType)) { @@ -173,75 +185,91 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk, // this is handled by TypeCastExpr if (Type::Equal(toType->GetReferenceTarget(), fromType->GetReferenceTarget()->GetAsConstType())) - return new TypeCastExpr(toType, this, false, pos); + goto typecast_ok; + + const ArrayType *atFrom = + dynamic_cast(fromType->GetReferenceTarget()); + const ArrayType *atTo = + dynamic_cast(toType->GetReferenceTarget()); - const ArrayType *atFrom = dynamic_cast(fromType->GetReferenceTarget()); - const ArrayType *atTo = dynamic_cast(toType->GetReferenceTarget()); if (atFrom != NULL && atTo != NULL && - Type::Equal(atFrom->GetElementType(), atTo->GetElementType())) - return new TypeCastExpr(toType, this, false, pos); - + Type::Equal(atFrom->GetElementType(), atTo->GetElementType())) { + goto typecast_ok; + } else { if (!failureOk) Error(pos, "Can't convert between incompatible reference types \"%s\" " "and \"%s\" for %s.", fromType->GetString().c_str(), toType->GetString().c_str(), errorMsgBase); - return NULL; + return false; } } else { // convert from a reference T -> T - Expr *fromExpr = new DereferenceExpr(this, pos); - if (fromExpr->GetType() == NULL) - return NULL; - return fromExpr->TypeConv(toType, errorMsgBase, failureOk); + if (expr != NULL) { + Expr *drExpr = new DereferenceExpr(*expr, pos); + if (lDoTypeConv(drExpr->GetType(), toType, &drExpr, failureOk, + errorMsgBase, pos) == true) { + *expr = drExpr; + return true; + } + return false; + } + else + return lDoTypeConv(fromType->GetReferenceTarget(), toType, NULL, + failureOk, errorMsgBase, pos); } } else if (dynamic_cast(toType)) { // T -> reference T - Expr *fromExpr = new ReferenceExpr(this, pos); - if (fromExpr->GetType() == NULL) - return NULL; - return fromExpr->TypeConv(toType, errorMsgBase, failureOk); + if (expr != NULL) { + Expr *rExpr = new ReferenceExpr(*expr, pos); + if (lDoTypeConv(rExpr->GetType(), toType, &rExpr, failureOk, + errorMsgBase, pos) == true) { + *expr = rExpr; + return true; + } + return false; + } + else + return lDoTypeConv(new ReferenceType(fromType, toType->IsConstType()), + toType, NULL, failureOk, errorMsgBase, pos); } else if (Type::Equal(toType, fromType->GetAsNonConstType())) // convert: const T -> T (as long as T isn't a reference) - return new TypeCastExpr(toType, this, false, pos); + goto typecast_ok; fromType = fromType->GetReferenceTarget(); toType = toType->GetReferenceTarget(); - // I don't think this is necessary -//CO if (Type::Equal(toType, fromType)) -//CO return fromExpr; - const ArrayType *toArrayType = dynamic_cast(toType); - const ArrayType *fromArrayType = dynamic_cast(fromType); if (toArrayType && fromArrayType) { if (Type::Equal(toArrayType->GetElementType(), fromArrayType->GetElementType())) { // the case of different element counts should have returned // out earlier, yes?? assert(toArrayType->GetElementCount() != fromArrayType->GetElementCount()); - return new TypeCastExpr(new ReferenceType(toType, false), this, - false, pos); + if (expr != NULL) + *expr = new TypeCastExpr(new ReferenceType(toType, false), + *expr, false, pos); + return true; } else if (Type::Equal(toArrayType->GetElementType(), fromArrayType->GetElementType()->GetAsConstType())) { // T[x] -> const T[x] - return new TypeCastExpr(new ReferenceType(toType, false), this, - false, pos); + if (expr != NULL) + *expr = new TypeCastExpr(new ReferenceType(toType, false), + *expr, false, pos); + return true; } else { if (!failureOk) Error(pos, "Array type \"%s\" can't be converted to type \"%s\" for %s.", fromType->GetString().c_str(), toType->GetString().c_str(), errorMsgBase); - return NULL; + return false; } } - const VectorType *toVectorType = dynamic_cast(toType); - const VectorType *fromVectorType = dynamic_cast(fromType); if (toVectorType && fromVectorType) { // converting e.g. int -> float if (fromVectorType->GetElementCount() != toVectorType->GetElementCount()) { @@ -249,13 +277,11 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk, Error(pos, "Can't convert between differently sized vector types " "\"%s\" -> \"%s\" for %s.", fromType->GetString().c_str(), toType->GetString().c_str(), errorMsgBase); - return NULL; + return false; } - return new TypeCastExpr(toType, this, false, pos); + goto typecast_ok; } - const StructType *toStructType = dynamic_cast(toType); - const StructType *fromStructType = dynamic_cast(fromType); if (toStructType && fromStructType) { if (!Type::Equal(toStructType->GetAsUniformType()->GetAsConstType(), fromStructType->GetAsUniformType()->GetAsConstType())) { @@ -263,14 +289,11 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk, Error(pos, "Can't convert between different struct types " "\"%s\" -> \"%s\".", fromStructType->GetString().c_str(), toStructType->GetString().c_str()); - return NULL; + return false; } - - return new TypeCastExpr(toType, this, false, pos); + goto typecast_ok; } - const EnumType *toEnumType = dynamic_cast(toType); - const EnumType *fromEnumType = dynamic_cast(fromType); if (toEnumType != NULL && fromEnumType != NULL) { // No implicit conversions between different enum types if (!Type::Equal(toEnumType->GetAsUniformType()->GetAsConstType(), @@ -279,19 +302,15 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk, Error(pos, "Can't convert between different enum types " "\"%s\" -> \"%s\".", fromEnumType->GetString().c_str(), toEnumType->GetString().c_str()); - return NULL; + return false; } - - return new TypeCastExpr(toType, this, false, pos); + goto typecast_ok; } - const AtomicType *toAtomicType = dynamic_cast(toType); - const AtomicType *fromAtomicType = dynamic_cast(fromType); - // enum -> atomic (integer, generally...) is always ok if (fromEnumType != NULL) { assert(toAtomicType != NULL || toVectorType != NULL); - return new TypeCastExpr(toType, this, false, pos); + goto typecast_ok; } // from here on out, the from type can only be atomic something or @@ -301,12 +320,12 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk, Error(pos, "Type conversion only possible from atomic types, not " "from \"%s\" to \"%s\", for %s.", fromType->GetString().c_str(), toType->GetString().c_str(), errorMsgBase); - return NULL; + return false; } // scalar -> short-vector conversions if (toVectorType != NULL) - return new TypeCastExpr(toType, this, false, pos); + goto typecast_ok; // ok, it better be a scalar->scalar conversion of some sort by now if (toAtomicType == NULL) { @@ -315,17 +334,34 @@ Expr::TypeConv(const Type *toType, const char *errorMsgBase, bool failureOk, "from \"%s\" to \"%s\", for %s.", fromType->GetString().c_str(), toType->GetString().c_str(), errorMsgBase); - return NULL; + return false; } -#if 0 - // Disable: it's not clear this is actually all that useful - if (!failureOk && issuePrecisionWarnings) - lMaybeIssuePrecisionWarning(toAtomicType, fromAtomicType, pos, - errorMsgBase); -#endif + typecast_ok: + if (expr != NULL) + *expr = new TypeCastExpr(toType, *expr, false, pos); + return true; +} - return new TypeCastExpr(toType, this, false, pos); + +bool +CanConvertTypes(const Type *fromType, const Type *toType) { + return lDoTypeConv(fromType, toType, NULL, true, NULL, SourcePos()); +} + + +Expr * +TypeConvertExpr(Expr *expr, const Type *toType, const char *errorMsgBase) { + if (expr == NULL) + return NULL; + + const Type *fromType = expr->GetType(); + Expr *e = expr; + if (lDoTypeConv(fromType, toType, &e, false, errorMsgBase, + expr->pos)) + return e; + else + return NULL; } @@ -770,8 +806,8 @@ UnaryExpr::TypeCheck() { } else if (op == LogicalNot) { const Type *boolType = lMatchingBoolType(type); - expr = expr->TypeConv(boolType, "logical not"); - if (!expr) + expr = TypeConvertExpr(expr, boolType, "logical not"); + if (expr == NULL) return NULL; } else if (op == BitNot) { @@ -1383,10 +1419,13 @@ BinaryExpr::TypeCheck() { bool isVarying = (type0->IsVaryingType() || type1->IsVaryingType()); if (isVarying) { - arg0 = arg0->TypeConv(type0->GetAsVaryingType(), "shift operator"); + arg0 = TypeConvertExpr(arg0, type0->GetAsVaryingType(), + "shift operator"); + if (arg0 == NULL) + return NULL; type0 = arg0->GetType(); } - arg1 = arg1->TypeConv(type0, "shift operator", false, false); + arg1 = TypeConvertExpr(arg1, type0, "shift operator"); if (arg1 == NULL) return NULL; } @@ -1396,8 +1435,8 @@ BinaryExpr::TypeCheck() { if (promotedType == NULL) return NULL; - arg0 = arg0->TypeConv(promotedType, "binary bit op"); - arg1 = arg1->TypeConv(promotedType, "binary bit op"); + arg0 = TypeConvertExpr(arg0, promotedType, "binary bit op"); + arg1 = TypeConvertExpr(arg1, promotedType, "binary bit op"); if (arg0 == NULL || arg1 == NULL) return NULL; } @@ -1431,9 +1470,9 @@ BinaryExpr::TypeCheck() { if (promotedType == NULL) return NULL; - arg0 = arg0->TypeConv(promotedType, lOpString(op)); - arg1 = arg1->TypeConv(promotedType, lOpString(op)); - if (!arg0 || !arg1) + arg0 = TypeConvertExpr(arg0, promotedType, lOpString(op)); + arg1 = TypeConvertExpr(arg1, promotedType, lOpString(op)); + if (arg0 == NULL || arg1 == NULL) return NULL; return this; } @@ -1459,9 +1498,9 @@ BinaryExpr::TypeCheck() { if (promotedType == NULL) return NULL; - arg0 = arg0->TypeConv(promotedType, lOpString(op)); - arg1 = arg1->TypeConv(promotedType, lOpString(op)); - if (!arg0 || !arg1) + arg0 = TypeConvertExpr(arg0, promotedType, lOpString(op)); + arg1 = TypeConvertExpr(arg1, promotedType, lOpString(op)); + if (arg0 == NULL || arg1 == NULL) return NULL; return this; } @@ -1490,10 +1529,10 @@ BinaryExpr::TypeCheck() { destType = new VectorType(boolType, vtype1->GetElementCount()); else destType = boolType; - - arg0 = arg0->TypeConv(destType, lOpString(op)); - arg1 = arg1->TypeConv(destType, lOpString(op)); - if (!arg0 || !arg1) + + arg0 = TypeConvertExpr(arg0, destType, lOpString(op)); + arg1 = TypeConvertExpr(arg1, destType, lOpString(op)); + if (arg0 == NULL || arg1 == NULL) return NULL; return this; } @@ -1726,9 +1765,11 @@ AssignExpr::TypeCheck() { lvalue = lvalue->TypeCheck(); if (rvalue != NULL) rvalue = rvalue->TypeCheck(); - if (rvalue != NULL && lvalue != NULL) - rvalue = rvalue->TypeConv(lvalue->GetType(), "assignment"); - if (rvalue == NULL || lvalue == NULL) + if (lvalue == NULL || rvalue == NULL) + return NULL; + + rvalue = TypeConvertExpr(rvalue, lvalue->GetType(), "assignment"); + if (rvalue == NULL) return NULL; if (lvalue->GetType()->IsConstType()) { @@ -1982,8 +2023,8 @@ SelectExpr::TypeCheck() { const Type *testType = test->GetType(); if (testType == NULL) return NULL; - test = test->TypeConv(lMatchingBoolType(testType), "select"); - if (testType == NULL) + test = TypeConvertExpr(test, lMatchingBoolType(testType), "select"); + if (test == NULL) return NULL; testType = test->GetType(); @@ -1994,9 +2035,9 @@ SelectExpr::TypeCheck() { if (promotedType == NULL) return NULL; - expr1 = expr1->TypeConv(promotedType, "select"); - expr2 = expr2->TypeConv(promotedType, "select"); - if (!expr1 || !expr2) + expr1 = TypeConvertExpr(expr1, promotedType, "select"); + expr2 = TypeConvertExpr(expr2, promotedType, "select"); + if (expr1 == NULL || expr2 == NULL) return NULL; return this; @@ -2099,7 +2140,8 @@ FunctionCallExpr::GetValue(FunctionEmitContext *ctx) const { } // Do whatever type conversion is needed - argExpr = argExpr->TypeConv(argTypes[i], "function call argument"); + argExpr = TypeConvertExpr(argExpr, argTypes[i], + "function call argument"); // The function overload resolution code should have ensured that // we can successfully do any type conversions needed here. assert(argExpr != NULL); @@ -2112,13 +2154,11 @@ FunctionCallExpr::GetValue(FunctionEmitContext *ctx) const { // FIXME: should we do this during type checking? const std::vector &argumentDefaults = ft->GetArgumentDefaults(); for (unsigned int i = callargs.size(); i < argumentDefaults.size(); ++i) { - assert(argumentDefaults[i] != NULL); - Expr *defaultExpr = argumentDefaults[i]->TypeConv(argTypes[i], - "function call default argument"); - if (defaultExpr == NULL) + Expr * d = TypeConvertExpr(argumentDefaults[i], argTypes[i], + "function call default argument"); + if (d == NULL) return NULL; - - callargs.push_back(defaultExpr); + callargs.push_back(d); } // Now evaluate the values of all of the parameters being passed. We @@ -2256,7 +2296,17 @@ FunctionCallExpr::TypeCheck() { return NULL; } - if (fse->ResolveOverloads(args->exprs) == true) { + std::vector argTypes; + for (unsigned int i = 0; i < args->exprs.size(); ++i) { + if (args->exprs[i] == NULL) + return NULL; + const Type *t = args->exprs[i]->GetType(); + if (t == NULL) + return NULL; + argTypes.push_back(t); + } + + if (fse->ResolveOverloads(argTypes) == true) { func = fse->TypeCheck(); if (func != NULL) { @@ -2271,9 +2321,9 @@ FunctionCallExpr::TypeCheck() { return NULL; launchCountExpr = - launchCountExpr->TypeConv(AtomicType::UniformInt32, - "task launch count"); - if (!launchCountExpr) + TypeConvertExpr(launchCountExpr, AtomicType::UniformInt32, + "task launch count"); + if (launchCountExpr == NULL) return NULL; } else { @@ -2621,8 +2671,8 @@ IndexExpr::TypeCheck() { !g->opt.disableUniformMemoryOptimizations); const Type *indexType = isUniform ? AtomicType::UniformInt32 : AtomicType::VaryingInt32; - index = index->TypeConv(indexType, "array index"); - if (!index) + index = TypeConvertExpr(index, indexType, "array index"); + if (index == NULL) return NULL; return this; @@ -5191,16 +5241,16 @@ lPrintFunctionOverloads(const std::string &name, static void -lPrintPassedTypes(const char *funName, const std::vector &argExprs) { +lPrintPassedTypes(const char *funName, + const std::vector &argTypes) { fprintf(stderr, "Passed types: %*c(", (int)strlen(funName), ' '); - for (unsigned int i = 0; i < argExprs.size(); ++i) { - const Type *t; - if (argExprs[i] != NULL && (t = argExprs[i]->GetType()) != NULL) - fprintf(stderr, "%s%s", t->GetString().c_str(), - (i < argExprs.size()-1) ? ", " : ")\n\n"); + for (unsigned int i = 0; i < argTypes.size(); ++i) { + if (argTypes[i] != NULL) + fprintf(stderr, "%s%s", argTypes[i]->GetString().c_str(), + (i < argTypes.size()-1) ? ", " : ")\n\n"); else fprintf(stderr, "(unknown type)%s", - (i < argExprs.size()-1) ? ", " : ")\n\n"); + (i < argTypes.size()-1) ? ", " : ")\n\n"); } } @@ -5211,9 +5261,7 @@ lPrintPassedTypes(const char *funName, const std::vector &argExprs) { failure. */ static int -lExactMatch(Expr *callArg, const Type *funcArgType) { - const Type *callType = callArg->GetType(); - +lExactMatch(const Type *callType, const Type *funcArgType) { if (dynamic_cast(callType) == NULL) callType = callType->GetAsNonConstType(); if (dynamic_cast(funcArgType) != NULL && @@ -5229,12 +5277,12 @@ lExactMatch(Expr *callArg, const Type *funcArgType) { modulo conversion to a reference type if needed. */ static int -lMatchIgnoringReferences(Expr *callArg, const Type *funcArgType) { - int prev = lExactMatch(callArg, funcArgType); +lMatchIgnoringReferences(const Type *callType, const Type *funcArgType) { + int prev = lExactMatch(callType, funcArgType); if (prev != -1) return prev; - const Type *callType = callArg->GetType()->GetReferenceTarget(); + callType = callType->GetReferenceTarget(); if (funcArgType->IsConstType()) callType = callType->GetAsConstType(); @@ -5247,12 +5295,11 @@ lMatchIgnoringReferences(Expr *callArg, const Type *funcArgType) { conversion that won't lose information. Otherwise reports failure. */ static int -lMatchWithTypeWidening(Expr *callArg, const Type *funcArgType) { - int prev = lMatchIgnoringReferences(callArg, funcArgType); +lMatchWithTypeWidening(const Type *callType, const Type *funcArgType) { + int prev = lMatchIgnoringReferences(callType, funcArgType); if (prev != -1) return prev; - const Type *callType = callArg->GetType(); const AtomicType *callAt = dynamic_cast(callType); const AtomicType *funcAt = dynamic_cast(funcArgType); if (callAt == NULL || funcAt == NULL) @@ -5299,12 +5346,11 @@ lMatchWithTypeWidening(Expr *callArg, const Type *funcArgType) { exactly the same type. */ static int -lMatchIgnoringUniform(Expr *callArg, const Type *funcArgType) { - int prev = lMatchWithTypeWidening(callArg, funcArgType); +lMatchIgnoringUniform(const Type *callType, const Type *funcArgType) { + int prev = lMatchWithTypeWidening(callType, funcArgType); if (prev != -1) return prev; - const Type *callType = callArg->GetType(); if (dynamic_cast(callType) == NULL) callType = callType->GetAsNonConstType(); @@ -5319,15 +5365,14 @@ lMatchIgnoringUniform(Expr *callArg, const Type *funcArgType) { argument type, but without doing a uniform -> varying conversion. */ static int -lMatchWithTypeConvSameVariability(Expr *callArg, const Type *funcArgType) { - int prev = lMatchIgnoringUniform(callArg, funcArgType); +lMatchWithTypeConvSameVariability(const Type *callType, + const Type *funcArgType) { + int prev = lMatchIgnoringUniform(callType, funcArgType); if (prev != -1) return prev; - Expr *te = callArg->TypeConv(funcArgType, - "function call argument", true); - if (te != NULL && - te->GetType()->IsUniformType() == callArg->GetType()->IsUniformType()) + if (CanConvertTypes(callType, funcArgType) && + (callType->IsUniformType() == funcArgType->IsUniformType())) return 1; else return -1; @@ -5339,14 +5384,12 @@ lMatchWithTypeConvSameVariability(Expr *callArg, const Type *funcArgType) { argument type to the function argument type. */ static int -lMatchWithTypeConv(Expr *callArg, const Type *funcArgType) { - int prev = lMatchWithTypeConvSameVariability(callArg, funcArgType); +lMatchWithTypeConv(const Type *callType, const Type *funcArgType) { + int prev = lMatchWithTypeConvSameVariability(callType, funcArgType); if (prev != -1) return prev; - Expr *te = callArg->TypeConv(funcArgType, - "function call argument", true); - return (te != NULL) ? 0 : -1; + return CanConvertTypes(callType, funcArgType) ? 0 : -1; } @@ -5383,8 +5426,8 @@ lGetBestMatch(std::vector > &matches) { finding multiple ambiguous matches. */ bool -FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *), - const std::vector &callArgs) { +FunctionSymbolExpr::tryResolve(int (*matchFunc)(const Type *, const Type *), + const std::vector &callTypes) { const char *funName = candidateFunctions->front()->name.c_str(); std::vector > matches; @@ -5401,7 +5444,7 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *), // There's no way to match if the caller is passing more arguments // than this function instance takes. - if (callArgs.size() > funcArgTypes.size()) + if (callTypes.size() > funcArgTypes.size()) continue; unsigned int i; @@ -5410,23 +5453,23 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *), // function than are passed, if the function has default argument // values. This case is handled below. int cost = 0; - for (i = 0; i < callArgs.size(); ++i) { + for (i = 0; i < callTypes.size(); ++i) { // This may happen if there's an error earlier in compilation. // It's kind of a silly to redundantly discover this for each // potential match versus detecting this earlier in the // matching process and just giving up. - if (!callArgs[i] || !callArgs[i]->GetType() || !funcArgTypes[i] || - dynamic_cast(callArgs[i]->GetType()) != NULL) + if (callTypes[i] == NULL || funcArgTypes[i] == NULL || + dynamic_cast(callTypes[i]) != NULL) return false; - int argCost = matchFunc(callArgs[i], funcArgTypes[i]); + int argCost = matchFunc(callTypes[i], funcArgTypes[i]); if (argCost == -1) // If the predicate function returns -1, we have failed no // matter what else happens, so we stop trying break; cost += argCost; } - if (i == callArgs.size()) { + if (i == callTypes.size()) { // All of the arguments matched! if (i == funcArgTypes.size()) // And we have exactly as many arguments as the function @@ -5454,7 +5497,7 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *), Error(pos, "Multiple overloaded instances of function \"%s\" matched.", funName); lPrintFunctionOverloads(funName, matches); - lPrintPassedTypes(funName, callArgs); + lPrintPassedTypes(funName, callTypes); // Stop trying to find more matches after an ambigious set of // matches. return true; @@ -5463,7 +5506,7 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(Expr *, const Type *), bool -FunctionSymbolExpr::ResolveOverloads(const std::vector &callArgs) { +FunctionSymbolExpr::ResolveOverloads(const std::vector &argTypes) { // Functions with names that start with "__" should only be various // builtins. For those, we'll demand an exact match, since we'll // expect whichever function in stdlib.ispc is calling out to one of @@ -5474,32 +5517,32 @@ FunctionSymbolExpr::ResolveOverloads(const std::vector &callArgs) { // Is there an exact match that doesn't require any argument type // conversion (other than converting type -> reference type)? - if (tryResolve(lExactMatch, callArgs)) + if (tryResolve(lExactMatch, argTypes)) return true; if (exactMatchOnly == false) { // Try to find a single match ignoring references - if (tryResolve(lMatchIgnoringReferences, callArgs)) + if (tryResolve(lMatchIgnoringReferences, argTypes)) return true; // Try to find an exact match via type widening--i.e. int8 -> // int16, etc.--things that don't lose data. - if (tryResolve(lMatchWithTypeWidening, callArgs)) + if (tryResolve(lMatchWithTypeWidening, argTypes)) return true; // Next try to see if there's a match via just uniform -> varying // promotions. - if (tryResolve(lMatchIgnoringUniform, callArgs)) + if (tryResolve(lMatchIgnoringUniform, argTypes)) return true; // Try to find a match via type conversion, but don't change // unif->varying if (tryResolve(lMatchWithTypeConvSameVariability, - callArgs)) + argTypes)) return true; // Last chance: try to find a match via arbitrary type conversion. - if (tryResolve(lMatchWithTypeConv, callArgs)) + if (tryResolve(lMatchWithTypeConv, argTypes)) return true; } @@ -5508,7 +5551,7 @@ FunctionSymbolExpr::ResolveOverloads(const std::vector &callArgs) { Error(pos, "Unable to find matching overload for call to function \"%s\"%s.", funName, exactMatchOnly ? " only considering exact matches" : ""); lPrintFunctionOverloads(funName, *candidateFunctions); - lPrintPassedTypes(funName, callArgs); + lPrintPassedTypes(funName, argTypes); return false; } diff --git a/expr.h b/expr.h index e571d88a..74a0f326 100644 --- a/expr.h +++ b/expr.h @@ -89,14 +89,6 @@ public: /** Prints the expression to standard output (used for debugging). */ virtual void Print() const = 0; - - /** This method tries to convert the expression to the given type. In - the event of failure, if the failureOk parameter is true, then no - error is issued. If failureOk is false, then an error is printed - that incorporates the given error message string. In either - failure case, NULL is returned. */ - Expr *TypeConv(const Type *type, const char *errorMsgBase = NULL, - bool failureOk = false, bool issuePrecisionWarnings = true); }; @@ -577,12 +569,12 @@ public: void Print() const; int EstimateCost() const; - bool ResolveOverloads(const std::vector &args); + bool ResolveOverloads(const std::vector &argTypes); Symbol *GetMatchingFunction(); private: - bool tryResolve(int (*matchFunc)(Expr *, const Type *), - const std::vector &args); + bool tryResolve(int (*matchFunc)(const Type *, const Type *), + const std::vector &argTypes); /** Name of the function that is being called. */ std::string name; @@ -611,4 +603,19 @@ public: int EstimateCost() const; }; + +/** This function indicates whether it's legal to convert from fromType to + toType. + */ +bool CanConvertTypes(const Type *fromType, const Type *toType); + +/** This function attempts to convert the given expression to the given + type, returning a pointer to a new expression that is the result. If + the required type conversion is illegal, it returns NULL and prints an + error message using the provided string to indicate the context for + which type conversion was being applied (e.g. "function call + parameter"). + */ +Expr *TypeConvertExpr(Expr *expr, const Type *toType, const char *errorMsgBase); + #endif // ISPC_EXPR_H diff --git a/module.cpp b/module.cpp index ee8078b0..b7f7dece 100644 --- a/module.cpp +++ b/module.cpp @@ -256,8 +256,8 @@ Module::AddGlobalVariable(Symbol *sym, Expr *initExpr, bool isConst) { // ExprList; they don't have types per se / can't type // convert themselves anyway.) if (dynamic_cast(initExpr) == NULL) - initExpr = initExpr->TypeConv(sym->type, "initializer"); - + initExpr = TypeConvertExpr(initExpr, sym->type, "initializer"); + if (initExpr != NULL) { initExpr = initExpr->Optimize(); // Fingers crossed, now let's see if we've got a diff --git a/stmt.cpp b/stmt.cpp index 1be6d3a6..65e895ff 100644 --- a/stmt.cpp +++ b/stmt.cpp @@ -146,9 +146,9 @@ lInitSymbol(llvm::Value *lvalue, const char *symName, const Type *type, // ExprList, then we'll see if we can type convert it to the type of // the variable. if (dynamic_cast(initExpr) == NULL) { - Expr *tcInit = initExpr->TypeConv(type, "inititalizer", true); - if (tcInit != NULL) { - llvm::Value *initializerValue = tcInit->GetValue(ctx); + initExpr = TypeConvertExpr(initExpr, type, "initializer"); + if (initExpr != NULL) { + llvm::Value *initializerValue = initExpr->GetValue(ctx); if (initializerValue != NULL) // Bingo; store the value in the variable's storage ctx->StoreInst(initializerValue, lvalue); @@ -380,10 +380,14 @@ DeclStmt::TypeCheck() { if (dynamic_cast(type) != NULL || dynamic_cast(type) != NULL) { // If it's an expr list with an atomic type, we'll later issue - // an error. Need to leave decl->initExpr as is in that case so it - // is in fact caught later, though. - if (dynamic_cast(vars[i].init) == NULL) - vars[i].init = vars[i].init->TypeConv(type, "initializer"); + // an error. Need to leave vars[i].init as is in that case so + // it is in fact caught later, though. + if (dynamic_cast(vars[i].init) == NULL) { + vars[i].init = TypeConvertExpr(vars[i].init, type, + "initializer"); + if (vars[i].init == NULL) + encounteredError = true; + } } } return encounteredError ? NULL : this;