Add implicit cast and null for passing arguments
parent
6b6eca61cb
commit
4a14a44f3f
|
@ -236,6 +236,7 @@ enum CBotError : int
|
||||||
CBotErrPrivate = 5041, //!< protected item
|
CBotErrPrivate = 5041, //!< protected item
|
||||||
CBotErrNoPublic = 5042, //!< missing word "public"
|
CBotErrNoPublic = 5042, //!< missing word "public"
|
||||||
CBotErrNoExpression = 5043, //!< expression expected after =
|
CBotErrNoExpression = 5043, //!< expression expected after =
|
||||||
|
CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function
|
||||||
|
|
||||||
// Runtime errors
|
// Runtime errors
|
||||||
CBotErrZeroDiv = 6000, //!< division by zero
|
CBotErrZeroDiv = 6000, //!< division by zero
|
||||||
|
|
|
@ -466,8 +466,7 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
|
||||||
|
|
||||||
if ( name.empty() ) return nullptr;
|
if ( name.empty() ) return nullptr;
|
||||||
|
|
||||||
int delta = 99999; // seeks the lowest signature
|
std::map<CBotFunction*, int> funcMap;
|
||||||
CBotFunction* pFunc = nullptr; // the best function found
|
|
||||||
|
|
||||||
if ( this != nullptr )
|
if ( this != nullptr )
|
||||||
{
|
{
|
||||||
|
@ -482,44 +481,48 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
|
||||||
CBotVar* pw = ppVars[i++]; // provided list parameter
|
CBotVar* pw = ppVars[i++]; // provided list parameter
|
||||||
while ( pv != nullptr && pw != nullptr)
|
while ( pv != nullptr && pw != nullptr)
|
||||||
{
|
{
|
||||||
if (!TypesCompatibles(pv->GetTypResult(), pw->GetTypResult()))
|
CBotTypResult paramType = pv->GetTypResult();
|
||||||
|
CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
||||||
|
|
||||||
|
if (!TypesCompatibles(paramType, argType))
|
||||||
{
|
{
|
||||||
if ( pFunc == nullptr ) TypeOrError = CBotErrBadParam;
|
if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
|
||||||
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
|
|
||||||
|
|
||||||
|
if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer))
|
||||||
|
{
|
||||||
|
CBotClass* c1 = paramType.GetClass();
|
||||||
|
CBotClass* c2 = argType.GetClass();
|
||||||
|
while (c2 != c1 && c2 != nullptr) // implicit cast
|
||||||
|
{
|
||||||
|
alpha += 10;
|
||||||
|
c2 = c2->GetParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
||||||
|
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
|
||||||
|
}
|
||||||
pv = pv->GetNext();
|
pv = pv->GetNext();
|
||||||
pw = ppVars[i++];
|
pw = ppVars[i++];
|
||||||
}
|
}
|
||||||
if ( pw != nullptr )
|
if ( pw != nullptr )
|
||||||
{
|
{
|
||||||
if ( pFunc != nullptr ) continue;
|
if ( !funcMap.empty() ) continue;
|
||||||
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
|
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
|
||||||
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
|
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
|
||||||
continue; // too many parameters
|
continue; // too many parameters
|
||||||
}
|
}
|
||||||
if ( pv != nullptr )
|
if ( pv != nullptr )
|
||||||
{
|
{
|
||||||
if ( pFunc != nullptr ) continue;
|
if ( !funcMap.empty() ) continue;
|
||||||
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
|
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
|
||||||
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
|
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
|
||||||
continue; // not enough parameters
|
continue; // not enough parameters
|
||||||
}
|
}
|
||||||
|
funcMap.insert( std::pair<CBotFunction*, int>(pt, alpha) );
|
||||||
if (alpha == 0) // perfect signature
|
|
||||||
{
|
|
||||||
nIdent = pt->m_nFuncIdent;
|
|
||||||
TypeOrError = pt->m_retTyp;
|
|
||||||
return pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( alpha < delta ) // a better signature?
|
|
||||||
{
|
|
||||||
pFunc = pt;
|
|
||||||
delta = alpha;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -537,50 +540,72 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
|
||||||
CBotVar* pw = ppVars[i++]; // list of provided parameters
|
CBotVar* pw = ppVars[i++]; // list of provided parameters
|
||||||
while ( pv != nullptr && pw != nullptr)
|
while ( pv != nullptr && pw != nullptr)
|
||||||
{
|
{
|
||||||
if (!TypesCompatibles(pv->GetTypResult(), pw->GetTypResult()))
|
CBotTypResult paramType = pv->GetTypResult();
|
||||||
|
CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
||||||
|
|
||||||
|
if (!TypesCompatibles(paramType, argType))
|
||||||
{
|
{
|
||||||
if ( pFunc == nullptr ) TypeOrError = CBotErrBadParam;
|
if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
|
||||||
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
|
|
||||||
|
|
||||||
|
if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer))
|
||||||
|
{
|
||||||
|
CBotClass* c1 = paramType.GetClass();
|
||||||
|
CBotClass* c2 = argType.GetClass();
|
||||||
|
while (c2 != c1 && c2 != nullptr) // implicit cast
|
||||||
|
{
|
||||||
|
alpha += 10;
|
||||||
|
c2 = c2->GetParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
||||||
|
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
|
||||||
|
}
|
||||||
pv = pv->GetNext();
|
pv = pv->GetNext();
|
||||||
pw = ppVars[i++];
|
pw = ppVars[i++];
|
||||||
}
|
}
|
||||||
if ( pw != nullptr )
|
if ( pw != nullptr )
|
||||||
{
|
{
|
||||||
if ( pFunc != nullptr ) continue;
|
if ( !funcMap.empty() ) continue; // previous useable function
|
||||||
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
|
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
|
||||||
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
|
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
|
||||||
continue; // to many parameters
|
continue; // to many parameters
|
||||||
}
|
}
|
||||||
if ( pv != nullptr )
|
if ( pv != nullptr )
|
||||||
{
|
{
|
||||||
if ( pFunc != nullptr ) continue;
|
if ( !funcMap.empty() ) continue; // previous useable function
|
||||||
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
|
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
|
||||||
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
|
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
|
||||||
continue; // not enough parameters
|
continue; // not enough parameters
|
||||||
}
|
}
|
||||||
|
funcMap.insert( std::pair<CBotFunction*, int>(pt, alpha) );
|
||||||
if (alpha == 0) // perfect signature
|
|
||||||
{
|
|
||||||
nIdent = pt->m_nFuncIdent;
|
|
||||||
TypeOrError = pt->m_retTyp;
|
|
||||||
return pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( alpha < delta ) // a better signature?
|
|
||||||
{
|
|
||||||
pFunc = pt;
|
|
||||||
delta = alpha;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( pFunc != nullptr )
|
if ( !funcMap.empty() )
|
||||||
{
|
{
|
||||||
|
auto it = funcMap.begin();
|
||||||
|
CBotFunction* pFunc = it->first; // the best function found
|
||||||
|
signed int delta = it->second; // seeks the lowest signature
|
||||||
|
|
||||||
|
for (++it ; it != funcMap.end() ; it++)
|
||||||
|
{
|
||||||
|
if (it->second < delta) // a better signature?
|
||||||
|
{
|
||||||
|
TypeOrError.SetType(CBotNoErr);
|
||||||
|
pFunc = it->first;
|
||||||
|
delta = it->second;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->second == delta) TypeOrError.SetType(CBotErrAmbiguousCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TypeOrError.Eq(CBotErrAmbiguousCall)) return nullptr;
|
||||||
nIdent = pFunc->m_nFuncIdent;
|
nIdent = pFunc->m_nFuncIdent;
|
||||||
TypeOrError = pFunc->m_retTyp;
|
TypeOrError = pFunc->m_retTyp;
|
||||||
return pFunc;
|
return pFunc;
|
||||||
|
|
|
@ -149,12 +149,20 @@ bool TypesCompatibles(const CBotTypResult& type1, const CBotTypResult& type2)
|
||||||
|
|
||||||
if (max >= CBotTypBoolean)
|
if (max >= CBotTypBoolean)
|
||||||
{
|
{
|
||||||
|
if (t1 == CBotTypPointer && t2 == CBotTypNullPointer) return true;
|
||||||
if (t2 != t1) return false;
|
if (t2 != t1) return false;
|
||||||
|
|
||||||
|
if (max == CBotTypPointer)
|
||||||
|
{
|
||||||
|
CBotClass* c1 = type1.GetClass();
|
||||||
|
CBotClass* c2 = type2.GetClass();
|
||||||
|
return c2->IsChildOf(c1);
|
||||||
|
}
|
||||||
|
|
||||||
if (max == CBotTypArrayPointer)
|
if (max == CBotTypArrayPointer)
|
||||||
return TypesCompatibles(type1.GetTypElem(), type2.GetTypElem());
|
return TypesCompatibles(type1.GetTypElem(), type2.GetTypElem());
|
||||||
|
|
||||||
if (max == CBotTypClass || max == CBotTypPointer)
|
if (max == CBotTypClass)
|
||||||
return type1.GetClass() == type2.GetClass() ;
|
return type1.GetClass() == type2.GetClass() ;
|
||||||
|
|
||||||
return true ;
|
return true ;
|
||||||
|
|
|
@ -717,6 +717,7 @@ void InitializeRestext()
|
||||||
stringsCbot[CBot::CBotErrPrivate] = TR("Private element");
|
stringsCbot[CBot::CBotErrPrivate] = TR("Private element");
|
||||||
stringsCbot[CBot::CBotErrNoPublic] = TR("Public required");
|
stringsCbot[CBot::CBotErrNoPublic] = TR("Public required");
|
||||||
stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after =");
|
stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after =");
|
||||||
|
stringsCbot[CBot::CBotErrAmbiguousCall] = TR("Ambiguous call to overloaded function");
|
||||||
|
|
||||||
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
|
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
|
||||||
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized");
|
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized");
|
||||||
|
|
|
@ -1786,3 +1786,91 @@ TEST_F(CBotUT, ClassNewConstructorMethodChain)
|
||||||
"}\n"
|
"}\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CBotUT, PassNullAsArgument)
|
||||||
|
{
|
||||||
|
auto publicProgram = ExecuteTest(
|
||||||
|
"public class BaseClass {}\n"
|
||||||
|
"public class SubClass extends BaseClass {}\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
ExecuteTest(
|
||||||
|
"bool Test(BaseClass b) {\n"
|
||||||
|
" return (b == null);\n"
|
||||||
|
"}\n"
|
||||||
|
"extern void PassNullAsArgument() {\n"
|
||||||
|
" ASSERT(true == Test(null));\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
ExecuteTest(
|
||||||
|
"void Test(BaseClass b) {}\n"
|
||||||
|
"void Test(SubClass s) {}\n"
|
||||||
|
"\n"
|
||||||
|
"extern void AmbiguousCallArgumentNull() {\n"
|
||||||
|
" Test(null);\n"
|
||||||
|
"}\n",
|
||||||
|
CBotErrAmbiguousCall
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CBotUT, ClassImplicitCastArguments)
|
||||||
|
{
|
||||||
|
auto publicProgram = ExecuteTest(
|
||||||
|
"public class BaseClass { int a = 360; }\n"
|
||||||
|
"public class SubClass extends BaseClass {}\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
ExecuteTest(
|
||||||
|
"bool Test(BaseClass b) {\n"
|
||||||
|
" SubClass s = b;\n"
|
||||||
|
" return (360 == s.a);\n"
|
||||||
|
"}\n"
|
||||||
|
"extern void UpcastPassingArguments() {\n"
|
||||||
|
" ASSERT(true == Test(new SubClass()));\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
ExecuteTest(
|
||||||
|
"void Test(BaseClass b, SubClass s) {}\n"
|
||||||
|
"void Test(SubClass s, BaseClass b) {}\n"
|
||||||
|
"\n"
|
||||||
|
"extern void UpcastAmbiguousCall() {\n"
|
||||||
|
" Test(new SubClass(), new SubClass());\n"
|
||||||
|
"}\n",
|
||||||
|
CBotErrAmbiguousCall
|
||||||
|
);
|
||||||
|
|
||||||
|
ExecuteTest(
|
||||||
|
"bool Test(BaseClass b, SubClass s) { return false; }\n"
|
||||||
|
"bool Test(SubClass s, BaseClass b) { return false; }\n"
|
||||||
|
"bool Test(SubClass s, SubClass s2) { return true; }\n"
|
||||||
|
"\n"
|
||||||
|
"extern void NoErrorMoreSpecific() {\n"
|
||||||
|
" ASSERT(true == Test(new SubClass(), new SubClass()));\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CBotUT, AmbiguousCallWithNumbers)
|
||||||
|
{
|
||||||
|
ExecuteTest(
|
||||||
|
"void Test(int i, float f) {}\n"
|
||||||
|
"void Test(float f, int i) {}\n"
|
||||||
|
"\n"
|
||||||
|
"extern void AmbiguousCallNumbers() {\n"
|
||||||
|
" Test(1, 2);\n"
|
||||||
|
"}\n",
|
||||||
|
CBotErrAmbiguousCall
|
||||||
|
);
|
||||||
|
|
||||||
|
ExecuteTest(
|
||||||
|
"bool Test(int i, float f) { return false; }\n"
|
||||||
|
"bool Test(float f, int i) { return false; }\n"
|
||||||
|
"bool Test(int i, int ii) { return true; }\n"
|
||||||
|
"\n"
|
||||||
|
"extern void NoErrorMoreSpecific() {\n"
|
||||||
|
" ASSERT(true == Test(1, 2));\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue