diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 2c88da92a..5e56bab56 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2489,7 +2489,7 @@ size_t PFunction::PropagateMark() // //========================================================================== -unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags) +unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags, int useflags) { Variant variant; @@ -2497,6 +2497,7 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray &argflags, TArra assert(Variants.Size() == 0); variant.Flags = flags; + variant.UseFlags = useflags; variant.Proto = proto; variant.ArgFlags = std::move(argflags); variant.ArgNames = std::move(argnames); diff --git a/src/dobjtype.h b/src/dobjtype.h index 1ca5df832..186cb8117 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -724,13 +724,14 @@ public: VMFunction *Implementation; TArray ArgFlags; // Should be the same length as Proto->ArgumentTypes TArray ArgNames; // we need the names to access them later when the function gets compiled. - DWORD Flags; + uint32_t Flags; + int UseFlags; PClass *SelfClass; }; TArray Variants; PClass *OwningClass = nullptr; - unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags); + unsigned AddVariant(PPrototype *proto, TArray &argflags, TArray &argnames, VMFunction *impl, int flags, int useflags); int GetImplicitArgs() { if (Variants[0].Flags & VARF_Action) return 3; diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 8909dd31b..b64e0c439 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -5931,6 +5931,42 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) return dest; } +//========================================================================== +// +// Checks if a function may be called from the current context. +// +//========================================================================== + +static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction *caller, PFunction *callee) +{ + if (callee->Variants[0].Flags & VARF_Method) + { + // The called function must support all usage modes of the current function. It may support more, but must not support less. + if ((callee->Variants[0].UseFlags & caller->Variants[0].UseFlags) != caller->Variants[0].UseFlags) + { + ScriptPosition.Message(MSG_ERROR, "Function %s incompatible with current context\n", callee->SymbolName.GetChars()); + return false; + } + + if (!(caller->Variants[0].Flags & VARF_Method)) + { + ScriptPosition.Message(MSG_ERROR, "Call to non-static function %s from a static context", callee->SymbolName.GetChars()); + return false; + } + else + { + auto callingself = caller->Variants[0].SelfClass; + auto calledself = callee->Variants[0].SelfClass; + if (!callingself->IsDescendantOf(calledself)) + { + ScriptPosition.Message(MSG_ERROR, "Call to member function %s with incompatible self pointer.", callee->SymbolName.GetChars()); + return false; + } + } + } + return true; +} + //========================================================================== // // @@ -6008,39 +6044,15 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) PFunction *afd = FindClassMemberFunction(ctx.Class, ctx.Class, MethodName, ScriptPosition, &error); - // Action functions in state providers need special treatment because self is of type Actor here. - if (afd != nullptr && ctx.Class->IsDescendantOf(RUNTIME_CLASS(AStateProvider)) && (ctx.Function->Variants[0].Flags & VARF_Action)) - { - // Only accept static and action functions from the current class. Calling a member function will require explicit use of 'invoker'. - if ((afd->Variants[0].Flags & (VARF_Method|VARF_Action)) == VARF_Method) - { - // Everything else that may be used here must pass the selfclass check, i.e. it must be reachable from Actor. - // Note that FuncClass is still the current item because for symbol privacy checks this is relevant. - afd = FindClassMemberFunction(ctx.Function->Variants[0].SelfClass, ctx.Class, MethodName, ScriptPosition, &error); - if (afd == nullptr) - { - ScriptPosition.Message(MSG_ERROR, "Unable to call non-action function %s from here. Please use 'invoker.%s' to call it.", MethodName.GetChars(), MethodName.GetChars()); - delete this; - return nullptr; - } - } - } - - if (error) - { - delete this; - return nullptr; - } - if (afd != nullptr) { - if (ctx.Function->Variants[0].Flags & VARF_Static && !(afd->Variants[0].Flags & VARF_Static)) + if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd)) { - ScriptPosition.Message(MSG_ERROR, "Call to non-static function %s from a static context", MethodName.GetChars()); delete this; return nullptr; } - auto self = !(afd->Variants[0].Flags & VARF_Static)? new FxSelf(ScriptPosition) : nullptr; + + auto self = (afd->Variants[0].Flags & VARF_Method)? new FxSelf(ScriptPosition) : nullptr; auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, false); delete this; return x->Resolve(ctx); @@ -6338,6 +6350,7 @@ isresolved: delete this; return nullptr; } + if (staticonly && (afd->Variants[0].Flags & VARF_Method)) { if (!ctx.Class->IsDescendantOf(cls)) @@ -6355,6 +6368,28 @@ isresolved: } } + if (afd->Variants[0].Flags & VARF_Method) + { + if (Self->ExprType == EFX_Self) + { + if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd)) + { + delete this; + return nullptr; + } + } + else + { + // Functions with no Actor usage may not be called through a pointer because they will lose their context. + if (!(afd->Variants[0].UseFlags & SUF_ACTOR)) + { + ScriptPosition.Message(MSG_ERROR, "Function %s cannot be used with a non-self object\n", afd->SymbolName.GetChars()); + delete this; + return nullptr; + } + } + } + // do not pass the self pointer to static functions. auto self = (afd->Variants[0].Flags & VARF_Method) ? Self : nullptr; auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, staticonly|novirtual); diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index bf6b08ea7..3abce216d 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -170,7 +170,7 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i SetImplicitArgs(&args, &argflags, &argnames, containingclass, fflags, flags); PFunction *sym = new PFunction(containingclass, NAME_None); // anonymous functions do not have names. - sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr, fflags); + sym->AddVariant(NewPrototype(rets, args), argflags, argnames, nullptr, fflags, flags); return sym; } diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index c24867837..89cc9487e 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -820,6 +820,7 @@ static void PrintFuncDeclarator(FLispString &out, ZCC_TreeNode *node) out.Break(); out.Open("func-declarator"); out.AddHex(dnode->Flags); + PrintNodes(out, dnode->UseFlags); PrintNodes(out, dnode->Type); out.AddName(dnode->Name); PrintNodes(out, dnode->Params); @@ -827,6 +828,16 @@ static void PrintFuncDeclarator(FLispString &out, ZCC_TreeNode *node) out.Close(); } +static void PrintDeclFlags(FLispString &out, ZCC_TreeNode *node) +{ + auto dnode = (ZCC_DeclFlags *)node; + out.Break(); + out.Open("decl-flags"); + out.AddHex(dnode->Flags); + PrintNodes(out, dnode->Id); + out.Close(); +} + static void PrintFlagStmt(FLispString &out, ZCC_TreeNode *node) { auto dnode = (ZCC_FlagStmt *)node; @@ -901,6 +912,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode * PrintFlagStmt, PrintPropertyStmt, PrintVectorInitializer, + PrintDeclFlags, }; FString ZCC_PrintAST(ZCC_TreeNode *root) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index a29d3d943..f152cfdd5 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -814,20 +814,21 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C). } else if (C.FuncName != NAME_None) { // A function - NEW_AST_NODE(FuncDeclarator, decl, A.SourceLoc); + NEW_AST_NODE(FuncDeclarator, decl, A == nullptr? C.SourceLoc : A->SourceLoc); decl->Type = B; decl->Params = C.FuncParams; decl->Name = C.FuncName; - decl->Flags = A.Int | C.FuncFlags; + decl->UseFlags = A == nullptr? nullptr : A->Id; + decl->Flags = (A == nullptr? 0 : A->Flags) | C.FuncFlags; decl->Body = C.FuncBody; X = decl; } else if (B != NULL && B->SiblingNext == B) { // A variable - NEW_AST_NODE(VarDeclarator, decl, A.SourceLoc); + NEW_AST_NODE(VarDeclarator, decl, A == nullptr? B->SourceLoc : A->SourceLoc); decl->Type = B; decl->Names = C.VarNames; - decl->Flags = A.Int; + decl->Flags = (A == nullptr? 0 : A->Flags); X = decl; } else @@ -845,10 +846,10 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C). } declarator_no_fun(X) ::= decl_flags(A) type(B) variable_list(C) SEMICOLON. { - NEW_AST_NODE(VarDeclarator, decl, A.SourceLoc ? A.SourceLoc : B->SourceLoc); + NEW_AST_NODE(VarDeclarator, decl, A != nullptr? A->SourceLoc : B->SourceLoc); decl->Type = B; decl->Names = C; - decl->Flags = A.Int; + decl->Flags = A == nullptr? 0 : A->Flags; X = decl; } @@ -916,19 +917,52 @@ variable_list(X) ::= variable_list(A) COMMA variable_name(B). X = A; /*X-overwrites-A*/ } -decl_flags(X) ::= . { X.Int = 0; X.SourceLoc = 0; } -decl_flags(X) ::= decl_flags(A) NATIVE(T). { X.Int = A.Int | ZCC_Native; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) STATIC(T). { X.Int = A.Int | ZCC_Static; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) PRIVATE(T). { X.Int = A.Int | ZCC_Private; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) PROTECTED(T). { X.Int = A.Int | ZCC_Protected; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) LATENT(T). { X.Int = A.Int | ZCC_Latent; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) FINAL(T). { X.Int = A.Int | ZCC_Final; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -decl_flags(X) ::= decl_flags(A) META(T). { X.Int = A.Int | ZCC_Meta; 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) 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; } +%type decl_flags { ZCC_DeclFlags * } +decl_flags(X) ::= . { X = NULL; } +decl_flags(X) ::= decl_flags(F) decl_flag(A). +{ + if (F == nullptr) + { + NEW_AST_NODE(DeclFlags,nil_f,A); + X = nil_f; + X->Id = nullptr; + X->Flags = A.Int; + } + else + { + X = F; + X->Flags |= A.Int; + } +} + + +decl_flags(X) ::= decl_flags(F) ACTION(B) states_opts(A). +{ + if (F == nullptr) + { + NEW_AST_NODE(DeclFlags,nil_f,B.SourceLoc); + X = nil_f; + X->Flags = ZCC_Action; + } + else + { + X = F; + X->Flags |= ZCC_Action; + } + X->Id = A; +} + +decl_flag(X) ::= NATIVE(T). { X.Int = ZCC_Native; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= STATIC(T). { X.Int = ZCC_Static; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= PRIVATE(T). { X.Int = ZCC_Private; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= PROTECTED(T). { X.Int = ZCC_Protected; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= LATENT(T). { X.Int = ZCC_Latent; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= FINAL(T). { X.Int = ZCC_Final; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= META(T). { X.Int = ZCC_Meta; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= OVERRIDE(T). { X.Int = ZCC_Override; X.SourceLoc = T.SourceLoc; } func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); } func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; } @@ -979,9 +1013,9 @@ func_param(X) ::= func_param_flags(A) type(B) IDENTIFIER(C) EQ expr(D). } func_param_flags(X) ::= . { X.Int = 0; X.SourceLoc = 0; } -func_param_flags(X) ::= func_param_flags(A) IN(T). { X.Int = A.Int | ZCC_In; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -func_param_flags(X) ::= func_param_flags(A) OUT(T). { X.Int = A.Int | ZCC_Out; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } -func_param_flags(X) ::= func_param_flags(A) OPTIONAL(T). { X.Int = A.Int | ZCC_Optional; X.SourceLoc = A.SourceLoc ? A.SourceLoc : T.SourceLoc; } +func_param_flags(X) ::= func_param_flags(A) IN(T). { X.Int = A.Int | ZCC_In; X.SourceLoc = T.SourceLoc; } +func_param_flags(X) ::= func_param_flags(A) OUT(T). { X.Int = A.Int | ZCC_Out; X.SourceLoc = T.SourceLoc; } +func_param_flags(X) ::= func_param_flags(A) OPTIONAL(T). { X.Int = A.Int | ZCC_Optional; X.SourceLoc = T.SourceLoc; } /************ Expressions ************/ diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 57475c50e..6cf719d3d 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2051,6 +2051,35 @@ void ZCCCompiler::InitFunctions() uint32_t varflags = VARF_Method; int implicitargs = 1; AFuncDesc *afd = nullptr; + int useflags = SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM; + if (f->UseFlags != nullptr) + { + useflags = 0; + auto p = f->UseFlags; + do + { + switch (p->Id) + { + case NAME_Actor: + useflags |= SUF_ACTOR; + break; + case NAME_Overlay: + useflags |= SUF_OVERLAY; + break; + case NAME_Weapon: + useflags |= SUF_WEAPON; + break; + case NAME_Item: + useflags |= SUF_ITEM; + break; + default: + Error(p, "Unknown Action qualifier %s", FName(p->Id).GetChars()); + break; + } + + p = static_cast(p->SiblingNext); + } while (p != f->UseFlags); + } // map to implementation flags. if (f->Flags & ZCC_Private) varflags |= VARF_Private; @@ -2058,9 +2087,22 @@ void ZCCCompiler::InitFunctions() 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_Final; // Action implies Final. + if (useflags & (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM)) + { + varflags |= VARF_Action; + implicitargs = 3; + } + else + { + implicitargs = 1; + } + } if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final. + 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 }; @@ -2096,9 +2138,7 @@ void ZCCCompiler::InitFunctions() (*afd->VMPointer)->ImplicitArgs = BYTE(implicitargs); } } - // Todo: parse these values from the definition - int tempuseflags = (varflags & VARF_Action) ? SUF_WEAPON | SUF_ITEM | SUF_OVERLAY | SUF_ACTOR : SUF_ACTOR; - SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, tempuseflags); + SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags, useflags); argdefaults.Resize(argnames.Size()); auto p = f->Params; bool hasoptionals = false; @@ -2225,7 +2265,7 @@ void ZCCCompiler::InitFunctions() } PFunction *sym = new PFunction(c->Type(), f->Name); - sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer), varflags); + sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer), varflags, useflags); c->Type()->Symbols.ReplaceSymbol(sym); if (!(f->Flags & ZCC_Native)) diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 1a187af82..ffff78cb8 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -101,6 +101,7 @@ enum EZCCTreeNodeType AST_FlagStmt, AST_PropertyStmt, AST_VectorValue, + AST_DeclFlags, NUM_AST_NODE_TYPES }; @@ -473,6 +474,12 @@ struct ZCC_FuncParamDecl : ZCC_TreeNode int Flags; }; +struct ZCC_DeclFlags : ZCC_TreeNode +{ + ZCC_Identifier *Id; + int Flags; +}; + struct ZCC_ConstantDef : ZCC_NamedNode { ZCC_Expression *Value; @@ -498,6 +505,7 @@ struct ZCC_FuncDeclarator : ZCC_Declarator ZCC_FuncParamDecl *Params; ENamedName Name; ZCC_Statement *Body; + ZCC_Identifier *UseFlags; }; struct ZCC_Default : ZCC_CompoundStmt