- allow skipping optional arguments of the parent function in a virtual override definition.

This is mainly to allow retroactive addition to existing virtual functions without breaking existing content.
The MeansOfDeath fix for Actor.Die would not be possible without such handling.
This commit is contained in:
Christoph Oelckers 2018-07-29 16:51:46 +02:00
parent ff69d945e1
commit 6634416b89
3 changed files with 29 additions and 7 deletions

View File

@ -776,16 +776,18 @@ PClass *PClass::FindClassTentative(FName name)
//
//==========================================================================
int PClass::FindVirtualIndex(FName name, PPrototype *proto)
int PClass::FindVirtualIndex(FName name, PFunction::Variant *variant, PFunction *parentfunc)
{
auto proto = variant->Proto;
for (unsigned i = 0; i < Virtuals.Size(); i++)
{
if (Virtuals[i]->Name == name)
{
auto vproto = Virtuals[i]->Proto;
if (vproto->ReturnTypes.Size() != proto->ReturnTypes.Size() ||
vproto->ArgumentTypes.Size() != proto->ArgumentTypes.Size())
vproto->ArgumentTypes.Size() < proto->ArgumentTypes.Size())
{
continue; // number of parameters does not match, so it's incompatible
}
bool fail = false;
@ -808,7 +810,26 @@ int PClass::FindVirtualIndex(FName name, PPrototype *proto)
break;
}
}
if (!fail) return i;
if (!fail)
{
if (vproto->ArgumentTypes.Size() > proto->ArgumentTypes.Size() && parentfunc)
{
// Check if the difference between both functions is only some optional arguments.
for (unsigned a = proto->ArgumentTypes.Size(); a < vproto->ArgumentTypes.Size(); a++)
{
if (!(parentfunc->Variants[0].ArgFlags[a] & VARF_Optional)) return -1;
}
// Todo: extend the prototype
for (unsigned a = proto->ArgumentTypes.Size(); a < vproto->ArgumentTypes.Size(); a++)
{
proto->ArgumentTypes.Push(vproto->ArgumentTypes[a]);
variant->ArgFlags.Push(parentfunc->Variants[0].ArgFlags[a]);
variant->ArgNames.Push(NAME_None);
}
}
return i;
}
}
}
return -1;

View File

@ -42,7 +42,7 @@ public:
void WriteAllFields(FSerializer &ar, const void *addr) const;
bool ReadAllFields(FSerializer &ar, void *addr) const;
void InitializeDefaults();
int FindVirtualIndex(FName name, PPrototype *proto);
int FindVirtualIndex(FName name, PFunction::Variant *variant, PFunction *parentfunc);
PSymbol *FindSymbol(FName symname, bool searchparents) const;
PField *AddField(FName name, PType *type, uint32_t flags);

View File

@ -2694,7 +2694,9 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (forclass)
{
int vindex = clstype->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
auto parentfunc = clstype->ParentClass? dyn_cast<PFunction>(clstype->ParentClass->VMType->Symbols.FindSymbol(sym->SymbolName, true)) : nullptr;
int vindex = clstype->FindVirtualIndex(sym->SymbolName, &sym->Variants[0], parentfunc);
// specifying 'override' is necessary to prevent one of the biggest problem spots with virtual inheritance: Mismatching argument types.
if (varflags & VARF_Override)
{
@ -2705,7 +2707,6 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
else
{
auto oldfunc = clstype->Virtuals[vindex];
auto parentfunc = dyn_cast<PFunction>(clstype->ParentClass->VMType->Symbols.FindSymbol(sym->SymbolName, true));
if (parentfunc && parentfunc->mVersion > mVersion)
{
Error(f, "Attempt to override function %s which is incompatible with version %d.%d.%d", FName(f->Name).GetChars(), mVersion.major, mVersion.minor, mVersion.revision);
@ -2765,7 +2766,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
}
else if (forclass)
{
int vindex = clstype->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
int vindex = clstype->FindVirtualIndex(sym->SymbolName, &sym->Variants[0], nullptr);
if (vindex != -1)
{
Error(f, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars());