From 4a14a44f3f07ee103161fbec2bcaa4bf5e6e2a84 Mon Sep 17 00:00:00 2001 From: melex750 Date: Sat, 17 Sep 2016 07:58:39 -0400 Subject: [PATCH] Add implicit cast and null for passing arguments --- src/CBot/CBotEnums.h | 1 + src/CBot/CBotInstr/CBotFunction.cpp | 107 ++++++++++++++++---------- src/CBot/CBotInstr/CBotInstrUtils.cpp | 10 ++- src/common/restext.cpp | 1 + test/unit/CBot/CBot_test.cpp | 88 +++++++++++++++++++++ 5 files changed, 165 insertions(+), 42 deletions(-) diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index 24e0095b..da0dbea7 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -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 diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index d00e8ea6..1e7e0b94 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -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 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(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(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; diff --git a/src/CBot/CBotInstr/CBotInstrUtils.cpp b/src/CBot/CBotInstr/CBotInstrUtils.cpp index bbe80b27..067f443a 100644 --- a/src/CBot/CBotInstr/CBotInstrUtils.cpp +++ b/src/CBot/CBotInstr/CBotInstrUtils.cpp @@ -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 ; diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 9770c32a..859a61f9 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -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"); diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 071abb94..2a112d77 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -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" + ); +}