Add implicit cast and null for passing arguments

dev-new-models
melex750 2016-09-17 07:58:39 -04:00
parent 6b6eca61cb
commit 4a14a44f3f
5 changed files with 165 additions and 42 deletions

View File

@ -236,6 +236,7 @@ enum CBotError : int
CBotErrPrivate = 5041, //!< protected item
CBotErrNoPublic = 5042, //!< missing word "public"
CBotErrNoExpression = 5043, //!< expression expected after =
CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function
// Runtime errors
CBotErrZeroDiv = 6000, //!< division by zero

View File

@ -466,8 +466,7 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
if ( name.empty() ) return nullptr;
int delta = 99999; // seeks the lowest signature
CBotFunction* pFunc = nullptr; // the best function found
std::map<CBotFunction*, int> funcMap;
if ( this != nullptr )
{
@ -482,44 +481,48 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
CBotVar* pw = ppVars[i++]; // provided list parameter
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;
}
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();
pw = ppVars[i++];
}
if ( pw != nullptr )
{
if ( pFunc != nullptr ) continue;
if ( !funcMap.empty() ) continue;
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
continue; // too many parameters
}
if ( pv != nullptr )
{
if ( pFunc != nullptr ) continue;
if ( !funcMap.empty() ) continue;
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
continue; // not enough parameters
}
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;
}
funcMap.insert( std::pair<CBotFunction*, int>(pt, alpha) );
}
}
}
@ -537,50 +540,72 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n
CBotVar* pw = ppVars[i++]; // list of provided parameters
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;
}
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();
pw = ppVars[i++];
}
if ( pw != nullptr )
{
if ( pFunc != nullptr ) continue;
if ( !funcMap.empty() ) continue; // previous useable function
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
continue; // to many parameters
}
if ( pv != nullptr )
{
if ( pFunc != nullptr ) continue;
if ( !funcMap.empty() ) continue; // previous useable function
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
continue; // not enough parameters
}
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;
}
funcMap.insert( std::pair<CBotFunction*, int>(pt, 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;
TypeOrError = pFunc->m_retTyp;
return pFunc;

View File

@ -149,12 +149,20 @@ bool TypesCompatibles(const CBotTypResult& type1, const CBotTypResult& type2)
if (max >= CBotTypBoolean)
{
if (t1 == CBotTypPointer && t2 == CBotTypNullPointer) return true;
if (t2 != t1) return false;
if (max == CBotTypPointer)
{
CBotClass* c1 = type1.GetClass();
CBotClass* c2 = type2.GetClass();
return c2->IsChildOf(c1);
}
if (max == CBotTypArrayPointer)
return TypesCompatibles(type1.GetTypElem(), type2.GetTypElem());
if (max == CBotTypClass || max == CBotTypPointer)
if (max == CBotTypClass)
return type1.GetClass() == type2.GetClass() ;
return true ;

View File

@ -717,6 +717,7 @@ void InitializeRestext()
stringsCbot[CBot::CBotErrPrivate] = TR("Private element");
stringsCbot[CBot::CBotErrNoPublic] = TR("Public required");
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::CBotErrNotInit] = TR("Variable not initialized");

View File

@ -1786,3 +1786,91 @@ TEST_F(CBotUT, ClassNewConstructorMethodChain)
"}\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"
);
}