diff --git a/src/namedef.h b/src/namedef.h index e9324d44b3..01859d01a8 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -683,6 +683,7 @@ xx(BuiltinFindMultiNameState) xx(BuiltinFindSingleNameState) xx(BuiltinHandleRuntimeState) xx(BuiltinGetDefault) +xx(BuiltinClassCast) xx(Damage) // basic type names diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 0c559e8501..d63db0f8d1 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -8382,6 +8382,107 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build) return dest; } +//========================================================================== +// +//========================================================================== + +FxClassPtrCast::FxClassPtrCast(PClass *dtype, FxExpression *x) + : FxExpression(EFX_ClassPtrCast, x->ScriptPosition) +{ + ValueType = NewClassPointer(dtype); + desttype = dtype; + basex = x; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxClassPtrCast::~FxClassPtrCast() +{ + SAFE_DELETE(basex); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxClassPtrCast::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(basex, ctx); + + if (basex->ValueType == TypeNullPtr) + { + basex->ValueType = ValueType; + auto x = basex; + basex = nullptr; + delete this; + return x; + } + auto to = static_cast(ValueType); + if (basex->ValueType->GetClass() == RUNTIME_CLASS(PClassPointer)) + { + auto from = static_cast(basex->ValueType); + // Downcast is always ok. + if (from->ClassRestriction->IsDescendantOf(to->ClassRestriction)) + { + basex->ValueType = to; + auto x = basex; + basex = nullptr; + delete this; + return x; + } + // Upcast needs a runtime check. + else if (to->ClassRestriction->IsDescendantOf(from->ClassRestriction)) + { + return this; + } + } + // Everything else is an error. + ScriptPosition.Message(MSG_ERROR, "Cannot cast %s to %s. The types are incompatible.", basex->ValueType->DescriptiveName(), to->DescriptiveName()); + delete this; + return nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +int BuiltinClassCast(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) +{ + PARAM_PROLOGUE; + PARAM_CLASS(from, DObject); + PARAM_CLASS(to, DObject); + ACTION_RETURN_OBJECT(from->IsDescendantOf(to) ? from : nullptr); +} + +ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build) +{ + ExpEmit clsname = basex->Emit(build); + + build->Emit(OP_PARAM, 0, clsname.RegType, clsname.RegNum); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(desttype, ATAG_OBJECT)); + + VMFunction *callfunc; + PSymbol *sym = FindBuiltinFunction(NAME_BuiltinClassCast, BuiltinClassCast); + + assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); + assert(((PSymbolVMFunction *)sym)->Function != nullptr); + callfunc = ((PSymbolVMFunction *)sym)->Function; + clsname.Free(build); + ExpEmit dest(build, REGT_POINTER); + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1); + build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum); + return dest; +} + //========================================================================== // // Symbolic state labels. diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index 84ddfcdd81..0ec3491bbb 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -258,6 +258,7 @@ enum EFxType EFX_JumpStatement, EFX_ReturnStatement, EFX_ClassTypeCast, + EFX_ClassPtrCast, EFX_StateByIndex, EFX_RuntimeStateIndex, EFX_MultiNameState, @@ -1668,6 +1669,25 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// +// +//========================================================================== + +class FxClassPtrCast : public FxExpression +{ + PClass *desttype; + FxExpression *basex; + +public: + + FxClassPtrCast(PClass *dtype, FxExpression *x); + ~FxClassPtrCast(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // Only used to resolve the old jump by index feature of DECORATE diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index 89cc9487e3..d8945ca3b0 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -585,6 +585,16 @@ static void PrintExprFuncCall(FLispString &out, ZCC_TreeNode *node) out.Close(); } +static void PrintExprClassCast(FLispString &out, ZCC_TreeNode *node) +{ + ZCC_ClassCast *enode = (ZCC_ClassCast *)node; + assert(enode->Operation == PEX_ClassCast); + out.Open("expr-class-cast"); + out.AddName(enode->ClassName); + PrintNodes(out, enode->Parameters, false); + out.Close(); +} + static void PrintExprMemberAccess(FLispString &out, ZCC_TreeNode *node) { ZCC_ExprMemberAccess *enode = (ZCC_ExprMemberAccess *)node; @@ -913,6 +923,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode * PrintPropertyStmt, PrintVectorInitializer, PrintDeclFlags, + PrintExprClassCast, }; FString ZCC_PrintAST(ZCC_TreeNode *root) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index f152cfdd58..c88cab481c 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -1080,6 +1080,14 @@ primary(X) ::= primary(A) LPAREN func_expr_list(B) RPAREN. [DOT] // Function ca expr->Parameters = B; X = expr; } +primary(X) ::= LPAREN CLASS LT IDENTIFIER(A) GT RPAREN LPAREN func_expr_list(B) RPAREN. [DOT] // class type cast +{ + NEW_AST_NODE(ClassCast, expr, A); + expr->Operation = PEX_ClassCast; + expr->ClassName = ENamedName(A.Int); + expr->Parameters = B; + X = expr; +} primary(X) ::= primary(A) LBRACKET expr(B) RBRACKET. [DOT] // Array access { NEW_AST_NODE(ExprBinary, expr, B); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 596b7d8c4d..baf76027ba 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2766,6 +2766,24 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) break; } + case AST_ClassCast: + { + auto cc = static_cast(ast); + if (cc->Parameters == nullptr || cc->Parameters->SiblingNext != cc->Parameters) + { + Error(cc, "Class type cast requires exactly one parameter"); + return new FxNop(*ast); // return something so that the compiler can continue. + } + auto cls = PClass::FindClass(cc->ClassName); + if (cls == nullptr) + { + Error(cc, "Unknown class %s", FName(cc->ClassName).GetChars()); + return new FxNop(*ast); // return something so that the compiler can continue. + } + return new FxClassPtrCast(cls, ConvertNode(cc->Parameters)); + } + + case AST_ExprMemberAccess: { auto memaccess = static_cast(ast); diff --git a/src/scripting/zscript/zcc_exprlist.h b/src/scripting/zscript/zcc_exprlist.h index b565c00fb2..faf6af6a43 100644 --- a/src/scripting/zscript/zcc_exprlist.h +++ b/src/scripting/zscript/zcc_exprlist.h @@ -8,6 +8,7 @@ xx(ConstValue, TK_Const) xx(FuncCall, '(') xx(ArrayAccess, TK_Array) xx(MemberAccess, '.') +xx(ClassCast, TK_Class) xx(TypeRef, TK_Class) xx(Vector, TK_Vector2) diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index ffff78cb83..070bef449d 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -102,6 +102,7 @@ enum EZCCTreeNodeType AST_PropertyStmt, AST_VectorValue, AST_DeclFlags, + AST_ClassCast, NUM_AST_NODE_TYPES }; @@ -366,6 +367,12 @@ struct ZCC_ExprFuncCall : ZCC_Expression ZCC_FuncParm *Parameters; }; +struct ZCC_ClassCast : ZCC_Expression +{ + ENamedName ClassName; + ZCC_FuncParm *Parameters; +}; + struct ZCC_ExprMemberAccess : ZCC_Expression { ZCC_Expression *Left;