mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-02-16 09:12:05 +00:00
- implemented handling for virtual function.
Syntax-wise I chose to make it as strict as possible to reduce the chance of errors: Virtual base functions must be declared with the 'virtual' keyword, and overrides in child classes with the 'override' keyword. This way any mismatch in parameters that otherwise would cause silent failure will outright produce a compile error.
This commit is contained in:
parent
a1aceaf04f
commit
72e77a6c65
15 changed files with 222 additions and 47 deletions
|
@ -3030,7 +3030,6 @@ void FinishDehPatch ()
|
||||||
while (subclass == nullptr);
|
while (subclass == nullptr);
|
||||||
|
|
||||||
AActor *defaults2 = GetDefaultByType (subclass);
|
AActor *defaults2 = GetDefaultByType (subclass);
|
||||||
memcpy ((void *)defaults2, (void *)defaults1, sizeof(AActor));
|
|
||||||
|
|
||||||
// Make a copy of the replaced class's state labels
|
// Make a copy of the replaced class's state labels
|
||||||
FStateDefinitions statedef;
|
FStateDefinitions statedef;
|
||||||
|
|
|
@ -156,14 +156,14 @@ PClassType::PClassType()
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// PClassType :: Derive
|
// PClassType :: DeriveData
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void PClassType::Derive(PClass *newclass)
|
void PClassType::DeriveData(PClass *newclass)
|
||||||
{
|
{
|
||||||
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassType)));
|
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassType)));
|
||||||
Super::Derive(newclass);
|
Super::DeriveData(newclass);
|
||||||
static_cast<PClassType *>(newclass)->TypeTableType = TypeTableType;
|
static_cast<PClassType *>(newclass)->TypeTableType = TypeTableType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3030,12 +3030,14 @@ void PClass::DestroySpecials(void *addr) const
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void PClass::Derive(PClass *newclass)
|
void PClass::Derive(PClass *newclass, FName name)
|
||||||
{
|
{
|
||||||
|
newclass->bRuntimeClass = true;
|
||||||
newclass->ParentClass = this;
|
newclass->ParentClass = this;
|
||||||
newclass->ConstructNative = ConstructNative;
|
newclass->ConstructNative = ConstructNative;
|
||||||
newclass->Symbols.SetParentTable(&this->Symbols);
|
newclass->Symbols.SetParentTable(&this->Symbols);
|
||||||
newclass->InitializeDefaults();
|
newclass->TypeName = name;
|
||||||
|
newclass->mDescriptiveName.Format("Class<%s>", name.GetChars());
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -3082,6 +3084,18 @@ void PClass::InitializeDefaults()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PClass :: DeriveData
|
||||||
|
//
|
||||||
|
// Copies inheritable data to the child class.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void PClass::DeriveData(PClass *newclass)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// PClass :: CreateDerivedClass
|
// PClass :: CreateDerivedClass
|
||||||
|
@ -3126,11 +3140,10 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
|
||||||
// Create a new type object of the same type as us. (We may be a derived class of PClass.)
|
// Create a new type object of the same type as us. (We may be a derived class of PClass.)
|
||||||
type = static_cast<PClass *>(GetClass()->CreateNew());
|
type = static_cast<PClass *>(GetClass()->CreateNew());
|
||||||
|
|
||||||
type->TypeName = name;
|
|
||||||
type->Size = size;
|
type->Size = size;
|
||||||
type->bRuntimeClass = true;
|
Derive(type, name);
|
||||||
type->mDescriptiveName.Format("Class<%s>", name.GetChars());
|
type->InitializeDefaults();
|
||||||
Derive(type);
|
type->Virtuals = Virtuals;
|
||||||
DeriveData(type);
|
DeriveData(type);
|
||||||
if (!notnew)
|
if (!notnew)
|
||||||
{
|
{
|
||||||
|
@ -3178,8 +3191,7 @@ PField *PClass::AddField(FName name, PType *type, DWORD flags)
|
||||||
// PClass :: FindClassTentative
|
// PClass :: FindClassTentative
|
||||||
//
|
//
|
||||||
// Like FindClass but creates a placeholder if no class is found.
|
// Like FindClass but creates a placeholder if no class is found.
|
||||||
// CreateDerivedClass will automatically fill in the placeholder when the
|
// This will be filled in when the actual class is constructed.
|
||||||
// actual class is defined.
|
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
|
@ -3201,17 +3213,59 @@ PClass *PClass::FindClassTentative(FName name, bool fatal)
|
||||||
PClass *type = static_cast<PClass *>(GetClass()->CreateNew());
|
PClass *type = static_cast<PClass *>(GetClass()->CreateNew());
|
||||||
DPrintf(DMSG_SPAMMY, "Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars());
|
DPrintf(DMSG_SPAMMY, "Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars());
|
||||||
|
|
||||||
type->TypeName = name;
|
Derive(type, name);
|
||||||
type->ParentClass = this;
|
|
||||||
type->ConstructNative = ConstructNative;
|
|
||||||
type->Size = TentativeClass;
|
type->Size = TentativeClass;
|
||||||
type->bRuntimeClass = true;
|
|
||||||
type->mDescriptiveName.Format("Class<%s>", name.GetChars());
|
|
||||||
type->Symbols.SetParentTable(&Symbols);
|
|
||||||
TypeTable.AddType(type, RUNTIME_CLASS(PClass), (intptr_t)type->Outer, name, bucket);
|
TypeTable.AddType(type, RUNTIME_CLASS(PClass), (intptr_t)type->Outer, name, bucket);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PClass :: FindVirtualIndex
|
||||||
|
//
|
||||||
|
// Compares a prototype with the existing list of virtual functions
|
||||||
|
// and returns an index if something matching is found.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int PClass::FindVirtualIndex(FName name, PPrototype *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())
|
||||||
|
{
|
||||||
|
continue; // number of parameters does not match, so it's incompatible
|
||||||
|
}
|
||||||
|
bool fail = false;
|
||||||
|
// The first argument is self and will mismatch so just skip it.
|
||||||
|
for (unsigned a = 1; a < proto->ArgumentTypes.Size(); a++)
|
||||||
|
{
|
||||||
|
if (proto->ArgumentTypes[a] != vproto->ArgumentTypes[a])
|
||||||
|
{
|
||||||
|
fail = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fail) continue;
|
||||||
|
|
||||||
|
for (unsigned a = 0; a < proto->ReturnTypes.Size(); a++)
|
||||||
|
{
|
||||||
|
if (proto->ReturnTypes[a] != vproto->ReturnTypes[a])
|
||||||
|
{
|
||||||
|
fail = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fail) return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// PClass :: BuildFlatPointers
|
// PClass :: BuildFlatPointers
|
||||||
|
|
|
@ -31,6 +31,7 @@ enum
|
||||||
VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures)
|
VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures)
|
||||||
VARF_Static = (1<<13), // static class data (by necessity read only.)
|
VARF_Static = (1<<13), // static class data (by necessity read only.)
|
||||||
VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code.
|
VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code.
|
||||||
|
VARF_Override = (1<<15), // overrides a virtual function from the parent class.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Symbol information -------------------------------------------------------
|
// Symbol information -------------------------------------------------------
|
||||||
|
@ -756,7 +757,7 @@ protected:
|
||||||
// We unravel _WITH_META here just as we did for PType.
|
// We unravel _WITH_META here just as we did for PType.
|
||||||
enum { MetaClassNum = CLASSREG_PClassClass };
|
enum { MetaClassNum = CLASSREG_PClassClass };
|
||||||
TArray<FTypeAndOffset> SpecialInits;
|
TArray<FTypeAndOffset> SpecialInits;
|
||||||
virtual void Derive(PClass *newclass);
|
void Derive(PClass *newclass, FName name);
|
||||||
void InitializeSpecials(void *addr) const;
|
void InitializeSpecials(void *addr) const;
|
||||||
void SetSuper();
|
void SetSuper();
|
||||||
public:
|
public:
|
||||||
|
@ -768,8 +769,9 @@ public:
|
||||||
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
|
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
|
||||||
bool ReadAllFields(FSerializer &ar, void *addr) const;
|
bool ReadAllFields(FSerializer &ar, void *addr) const;
|
||||||
void InitializeDefaults();
|
void InitializeDefaults();
|
||||||
|
int FindVirtualIndex(FName name, PPrototype *proto);
|
||||||
|
virtual void DeriveData(PClass *newclass);
|
||||||
|
|
||||||
virtual void DeriveData(PClass *newclass) {}
|
|
||||||
static void StaticInit();
|
static void StaticInit();
|
||||||
static void StaticShutdown();
|
static void StaticShutdown();
|
||||||
static void StaticBootstrap();
|
static void StaticBootstrap();
|
||||||
|
@ -782,6 +784,7 @@ public:
|
||||||
BYTE *Defaults;
|
BYTE *Defaults;
|
||||||
bool bRuntimeClass; // class was defined at run-time, not compile-time
|
bool bRuntimeClass; // class was defined at run-time, not compile-time
|
||||||
bool bExported; // This type has been declared in a script
|
bool bExported; // This type has been declared in a script
|
||||||
|
TArray<VMFunction*> Virtuals; // virtual function table
|
||||||
|
|
||||||
void (*ConstructNative)(void *);
|
void (*ConstructNative)(void *);
|
||||||
|
|
||||||
|
@ -838,7 +841,7 @@ class PClassType : public PClass
|
||||||
protected:
|
protected:
|
||||||
public:
|
public:
|
||||||
PClassType();
|
PClassType();
|
||||||
virtual void Derive(PClass *newclass);
|
virtual void DeriveData(PClass *newclass);
|
||||||
|
|
||||||
PClass *TypeTableType; // The type to use for hashing into the type table
|
PClass *TypeTableType; // The type to use for hashing into the type table
|
||||||
};
|
};
|
||||||
|
|
|
@ -168,6 +168,7 @@ std2:
|
||||||
'optional' { RET(TK_Optional); }
|
'optional' { RET(TK_Optional); }
|
||||||
'export' { RET(TK_Export); }
|
'export' { RET(TK_Export); }
|
||||||
'virtual' { RET(TK_Virtual); }
|
'virtual' { RET(TK_Virtual); }
|
||||||
|
'override' { RET(TK_Override); }
|
||||||
'super' { RET(TK_Super); }
|
'super' { RET(TK_Super); }
|
||||||
'global' { RET(TK_Global); }
|
'global' { RET(TK_Global); }
|
||||||
'stop' { RET(TK_Stop); }
|
'stop' { RET(TK_Stop); }
|
||||||
|
|
|
@ -105,6 +105,7 @@ xx(TK_Iterator, "'iterator'")
|
||||||
xx(TK_Optional, "'optional'")
|
xx(TK_Optional, "'optional'")
|
||||||
xx(TK_Export, "'expert'")
|
xx(TK_Export, "'expert'")
|
||||||
xx(TK_Virtual, "'virtual'")
|
xx(TK_Virtual, "'virtual'")
|
||||||
|
xx(TK_Override, "'override'")
|
||||||
xx(TK_Super, "'super'")
|
xx(TK_Super, "'super'")
|
||||||
xx(TK_Null, "'null'")
|
xx(TK_Null, "'null'")
|
||||||
xx(TK_Global, "'global'")
|
xx(TK_Global, "'global'")
|
||||||
|
|
|
@ -6693,12 +6693,16 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VMFunction *vmfunc = Function->Variants[0].Implementation;
|
||||||
|
bool staticcall = (vmfunc->Final || vmfunc->VirtualIndex == -1 || NoVirtual);
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
// Emit code to pass implied parameters
|
// Emit code to pass implied parameters
|
||||||
|
ExpEmit selfemit;
|
||||||
if (Function->Variants[0].Flags & VARF_Method)
|
if (Function->Variants[0].Flags & VARF_Method)
|
||||||
{
|
{
|
||||||
assert(Self != nullptr);
|
assert(Self != nullptr);
|
||||||
ExpEmit selfemit = Self->Emit(build);
|
selfemit = Self->Emit(build);
|
||||||
assert(selfemit.RegType == REGT_POINTER);
|
assert(selfemit.RegType == REGT_POINTER);
|
||||||
build->Emit(OP_PARAM, 0, selfemit.RegType, selfemit.RegNum);
|
build->Emit(OP_PARAM, 0, selfemit.RegType, selfemit.RegNum);
|
||||||
count += 1;
|
count += 1;
|
||||||
|
@ -6718,8 +6722,9 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
||||||
}
|
}
|
||||||
count += 2;
|
count += 2;
|
||||||
}
|
}
|
||||||
selfemit.Free(build);
|
if (staticcall) selfemit.Free(build);
|
||||||
}
|
}
|
||||||
|
else staticcall = true;
|
||||||
// Emit code to pass explicit parameters
|
// Emit code to pass explicit parameters
|
||||||
for (unsigned i = 0; i < ArgList.Size(); ++i)
|
for (unsigned i = 0; i < ArgList.Size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -6729,7 +6734,8 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
||||||
ArgList.ShrinkToFit();
|
ArgList.ShrinkToFit();
|
||||||
|
|
||||||
// Get a constant register for this function
|
// Get a constant register for this function
|
||||||
VMFunction *vmfunc = Function->Variants[0].Implementation;
|
if (staticcall)
|
||||||
|
{
|
||||||
int funcaddr = build->GetConstantAddress(vmfunc, ATAG_OBJECT);
|
int funcaddr = build->GetConstantAddress(vmfunc, ATAG_OBJECT);
|
||||||
// Emit the call
|
// Emit the call
|
||||||
if (EmitTail)
|
if (EmitTail)
|
||||||
|
@ -6752,6 +6758,34 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
|
||||||
return ExpEmit();
|
return ExpEmit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selfemit.Free(build);
|
||||||
|
ExpEmit funcreg(build, REGT_POINTER);
|
||||||
|
build->Emit(OP_VTBL, funcreg.RegNum, selfemit.RegNum, vmfunc->VirtualIndex);
|
||||||
|
if (EmitTail)
|
||||||
|
{ // Tail call
|
||||||
|
build->Emit(OP_TAIL, funcreg.RegNum, count, 0);
|
||||||
|
ExpEmit call;
|
||||||
|
call.Final = true;
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
else if (vmfunc->Proto->ReturnTypes.Size() > 0)
|
||||||
|
{ // Call, expecting one result
|
||||||
|
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount());
|
||||||
|
build->Emit(OP_CALL, funcreg.RegNum, count, 1);
|
||||||
|
build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Call, expecting no results
|
||||||
|
build->Emit(OP_CALL, funcreg.RegNum, count, 0);
|
||||||
|
return ExpEmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
|
|
|
@ -655,7 +655,9 @@ class VMFunction : public DObject
|
||||||
HAS_OBJECT_POINTERS;
|
HAS_OBJECT_POINTERS;
|
||||||
public:
|
public:
|
||||||
bool Native;
|
bool Native;
|
||||||
|
bool Final = false; // cannot be overridden
|
||||||
BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
|
BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action
|
||||||
|
int VirtualIndex = -1;
|
||||||
FName Name;
|
FName Name;
|
||||||
TArray<VMValue> DefaultArgs;
|
TArray<VMValue> DefaultArgs;
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
#define RPI8 MODE_AP | MODE_BIMMZ | MODE_CUNUSED
|
#define RPI8 MODE_AP | MODE_BIMMZ | MODE_CUNUSED
|
||||||
#define KPI8 MODE_AKP | MODE_BIMMZ | MODE_CUNUSED
|
#define KPI8 MODE_AKP | MODE_BIMMZ | MODE_CUNUSED
|
||||||
#define RPI8I8 MODE_AP | MODE_BIMMZ | MODE_CIMMZ
|
#define RPI8I8 MODE_AP | MODE_BIMMZ | MODE_CIMMZ
|
||||||
|
#define RPRPI8 MODE_AP | MODE_BP | MODE_CIMMZ
|
||||||
#define KPI8I8 MODE_AKP | MODE_BIMMZ | MODE_CIMMZ
|
#define KPI8I8 MODE_AKP | MODE_BIMMZ | MODE_CIMMZ
|
||||||
#define I8BCP MODE_AIMMZ | MODE_BCJOINT | MODE_BCPARAM
|
#define I8BCP MODE_AIMMZ | MODE_BCJOINT | MODE_BCPARAM
|
||||||
#define THROW MODE_AIMMZ | MODE_BCTHROW
|
#define THROW MODE_AIMMZ | MODE_BCTHROW
|
||||||
|
|
|
@ -525,6 +525,15 @@ begin:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NEXTOP;
|
NEXTOP;
|
||||||
|
OP(VTBL):
|
||||||
|
ASSERTA(a); ASSERTA(B);
|
||||||
|
{
|
||||||
|
auto o = (DObject*)reg.a[B];
|
||||||
|
auto p = o->GetClass();
|
||||||
|
assert(C < p->Virtuals.Size());
|
||||||
|
reg.a[a] = p->Virtuals[C];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
OP(CALL_K):
|
OP(CALL_K):
|
||||||
ASSERTKA(a);
|
ASSERTKA(a);
|
||||||
assert(konstatag[a] == ATAG_OBJECT);
|
assert(konstatag[a] == ATAG_OBJECT);
|
||||||
|
|
|
@ -82,6 +82,7 @@ xx(PARAM, param, __BCP), // push parameter encoded in BC for function call (B=r
|
||||||
xx(PARAMI, parami, I24), // push immediate, signed integer for function call
|
xx(PARAMI, parami, I24), // push immediate, signed integer for function call
|
||||||
xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C
|
xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C
|
||||||
xx(CALL_K, call, KPI8I8),
|
xx(CALL_K, call, KPI8I8),
|
||||||
|
xx(VTBL, vtbl, RPRPI8), // dereferences a virtual method table.
|
||||||
xx(TAIL, tail, RPI8), // Call+Ret in a single instruction
|
xx(TAIL, tail, RPI8), // Call+Ret in a single instruction
|
||||||
xx(TAIL_K, tail, KPI8),
|
xx(TAIL_K, tail, KPI8),
|
||||||
xx(RESULT, result, __BCP), // Result should go in register encoded in BC (in caller, after CALL)
|
xx(RESULT, result, __BCP), // Result should go in register encoded in BC (in caller, after CALL)
|
||||||
|
|
|
@ -905,6 +905,8 @@ decl_flags(X) ::= decl_flags(A) META(T). { X.Int = A.Int | ZCC_Meta; X.SourceLo
|
||||||
decl_flags(X) ::= decl_flags(A) ACTION(T). { X.Int = A.Int | ZCC_Action; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; }
|
decl_flags(X) ::= decl_flags(A) ACTION(T). { X.Int = A.Int | ZCC_Action; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; }
|
||||||
decl_flags(X) ::= decl_flags(A) READONLY(T). { X.Int = A.Int | ZCC_ReadOnly; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; }
|
decl_flags(X) ::= decl_flags(A) READONLY(T). { X.Int = A.Int | ZCC_ReadOnly; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; }
|
||||||
decl_flags(X) ::= decl_flags(A) DEPRECATED(T). { X.Int = A.Int | ZCC_Deprecated; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; }
|
decl_flags(X) ::= decl_flags(A) DEPRECATED(T). { X.Int = A.Int | ZCC_Deprecated; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; }
|
||||||
|
decl_flags(X) ::= decl_flags(A) VIRTUAL(T). { X.Int = A.Int | ZCC_Virtual; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; }
|
||||||
|
decl_flags(X) ::= decl_flags(A) OVERRIDE(T). { X.Int = A.Int | ZCC_Override; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; }
|
||||||
|
|
||||||
func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); }
|
func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); }
|
||||||
func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; }
|
func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; }
|
||||||
|
|
|
@ -1258,7 +1258,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
|
||||||
PType *fieldtype = DetermineType(type, field, field->Names->Name, field->Type, true, true);
|
PType *fieldtype = DetermineType(type, field, field->Names->Name, field->Type, true, true);
|
||||||
|
|
||||||
// For structs only allow 'deprecated', for classes exclude function qualifiers.
|
// For structs only allow 'deprecated', for classes exclude function qualifiers.
|
||||||
int notallowed = forstruct? ~ZCC_Deprecated : ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract;
|
int notallowed = forstruct? ~ZCC_Deprecated : ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override;
|
||||||
|
|
||||||
if (field->Flags & notallowed)
|
if (field->Flags & notallowed)
|
||||||
{
|
{
|
||||||
|
@ -1894,6 +1894,7 @@ void ZCCCompiler::InitDefaults()
|
||||||
if (!c->Type()->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
if (!c->Type()->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||||
{
|
{
|
||||||
if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->Type()->TypeName.GetChars());
|
if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->Type()->TypeName.GetChars());
|
||||||
|
if (c->Type()->ParentClass) c->Type()->ParentClass->DeriveData(c->Type());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1989,6 +1990,16 @@ void ZCCCompiler::InitFunctions()
|
||||||
|
|
||||||
for (auto c : Classes)
|
for (auto c : Classes)
|
||||||
{
|
{
|
||||||
|
// cannot be done earlier because it requires the parent class to be processed by this code, too.
|
||||||
|
if (c->Type()->ParentClass != nullptr)
|
||||||
|
{
|
||||||
|
if (c->Type()->ParentClass->Virtuals.Size() == 0)
|
||||||
|
{
|
||||||
|
// This a VMClass which didn't get processed here.
|
||||||
|
c->Type()->ParentClass->Virtuals = c->Type()->ParentClass->ParentClass->Virtuals;
|
||||||
|
}
|
||||||
|
c->Type()->Virtuals = c->Type()->ParentClass->Virtuals;
|
||||||
|
}
|
||||||
for (auto f : c->Functions)
|
for (auto f : c->Functions)
|
||||||
{
|
{
|
||||||
rets.Clear();
|
rets.Clear();
|
||||||
|
@ -2030,13 +2041,32 @@ void ZCCCompiler::InitFunctions()
|
||||||
if (f->Flags & ZCC_Private) varflags |= VARF_Private;
|
if (f->Flags & ZCC_Private) varflags |= VARF_Private;
|
||||||
if (f->Flags & ZCC_Protected) varflags |= VARF_Protected;
|
if (f->Flags & ZCC_Protected) varflags |= VARF_Protected;
|
||||||
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
|
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
|
||||||
|
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
|
||||||
|
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
|
||||||
if (f->Flags & ZCC_Action) varflags |= VARF_Action|VARF_Final, implicitargs = 3; // Action implies Final.
|
if (f->Flags & ZCC_Action) varflags |= VARF_Action|VARF_Final, implicitargs = 3; // Action implies Final.
|
||||||
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
|
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final.
|
||||||
if ((f->Flags & (ZCC_Action | ZCC_Static)) == (ZCC_Action | ZCC_Static))
|
|
||||||
|
if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'.
|
||||||
|
// Only one of these flags may be used.
|
||||||
|
static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static };
|
||||||
|
static const char * print[] = { "virtual", "override", "action", "static" };
|
||||||
|
int fc = 0;
|
||||||
|
FString build;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
Error(f, "%s: Action and Static on the same function is not allowed.", FName(f->Name).GetChars());
|
if (f->Flags & exclude[i])
|
||||||
|
{
|
||||||
|
fc++;
|
||||||
|
if (build.Len() > 0) build += ", ";
|
||||||
|
build += print[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fc > 1)
|
||||||
|
{
|
||||||
|
Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars() );
|
||||||
varflags |= VARF_Method;
|
varflags |= VARF_Method;
|
||||||
}
|
}
|
||||||
|
if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well.
|
||||||
|
|
||||||
if (f->Flags & ZCC_Native)
|
if (f->Flags & ZCC_Native)
|
||||||
{
|
{
|
||||||
|
@ -2193,7 +2223,41 @@ void ZCCCompiler::InitFunctions()
|
||||||
{
|
{
|
||||||
sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults);
|
sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults);
|
||||||
}
|
}
|
||||||
// todo: Check inheritance.
|
|
||||||
|
if (varflags & VARF_Virtual)
|
||||||
|
{
|
||||||
|
if (varflags & VARF_Final)
|
||||||
|
{
|
||||||
|
sym->Variants[0].Implementation->Final = true;
|
||||||
|
}
|
||||||
|
int vindex = c->Type()->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto);
|
||||||
|
// specifying 'override' is necessary to prevent one of the biggest problem spots with virtual inheritance: Mismatching argument types.
|
||||||
|
if (varflags & VARF_Override)
|
||||||
|
{
|
||||||
|
if (vindex == -1)
|
||||||
|
{
|
||||||
|
Error(p, "Attempt to override non-existent virtual function %s", FName(f->Name).GetChars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto oldfunc = c->Type()->Virtuals[vindex];
|
||||||
|
if (oldfunc->Final)
|
||||||
|
{
|
||||||
|
Error(p, "Attempt to override final function %s", FName(f->Name).GetChars());
|
||||||
|
}
|
||||||
|
c->Type()->Virtuals[vindex] = sym->Variants[0].Implementation;
|
||||||
|
sym->Variants[0].Implementation->VirtualIndex = vindex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (vindex != -1)
|
||||||
|
{
|
||||||
|
Error(p, "Function %s attempts to override parent function without 'override' qualifier", FName(f->Name).GetChars());
|
||||||
|
}
|
||||||
|
sym->Variants[0].Implementation->VirtualIndex = c->Type()->Virtuals.Push(sym->Variants[0].Implementation);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,8 @@ static void InitTokenMap()
|
||||||
TOKENDEF (TK_Private, ZCC_PRIVATE);
|
TOKENDEF (TK_Private, ZCC_PRIVATE);
|
||||||
TOKENDEF (TK_Protected, ZCC_PROTECTED);
|
TOKENDEF (TK_Protected, ZCC_PROTECTED);
|
||||||
TOKENDEF (TK_Latent, ZCC_LATENT);
|
TOKENDEF (TK_Latent, ZCC_LATENT);
|
||||||
|
TOKENDEF (TK_Virtual, ZCC_VIRTUAL);
|
||||||
|
TOKENDEF (TK_Override, ZCC_OVERRIDE);
|
||||||
TOKENDEF (TK_Final, ZCC_FINAL);
|
TOKENDEF (TK_Final, ZCC_FINAL);
|
||||||
TOKENDEF (TK_Meta, ZCC_META);
|
TOKENDEF (TK_Meta, ZCC_META);
|
||||||
TOKENDEF (TK_Deprecated, ZCC_DEPRECATED);
|
TOKENDEF (TK_Deprecated, ZCC_DEPRECATED);
|
||||||
|
|
|
@ -33,6 +33,8 @@ enum
|
||||||
ZCC_FuncConst = 1 << 10,
|
ZCC_FuncConst = 1 << 10,
|
||||||
ZCC_Abstract = 1 << 11,
|
ZCC_Abstract = 1 << 11,
|
||||||
ZCC_Extension = 1 << 12,
|
ZCC_Extension = 1 << 12,
|
||||||
|
ZCC_Virtual = 1 << 13,
|
||||||
|
ZCC_Override = 1 << 14,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function parameter modifiers
|
// Function parameter modifiers
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class Object native
|
class Object native
|
||||||
{
|
{
|
||||||
/*virtual*/ native void Destroy();
|
virtual native void Destroy();
|
||||||
native class<Object> GetClass();
|
native class<Object> GetClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue