diff --git a/src/common/scripting/core/types.h b/src/common/scripting/core/types.h index 5dc8fd182..380ae88d4 100644 --- a/src/common/scripting/core/types.h +++ b/src/common/scripting/core/types.h @@ -111,6 +111,8 @@ public: bool SizeKnown = true; bool TypeInternal = false; + bool TypeDeprecated = false; // mVersion is deprecation version, not minimum version + FString mDeprecationMessage; PType * LocalType = nullptr; diff --git a/src/common/scripting/frontend/zcc-parse.lemon b/src/common/scripting/frontend/zcc-parse.lemon index c6781db12..eed755355 100644 --- a/src/common/scripting/frontend/zcc-parse.lemon +++ b/src/common/scripting/frontend/zcc-parse.lemon @@ -80,6 +80,7 @@ static void SetNodeLine(ZCC_TreeNode *name, int line) ZCC_Identifier *Replaces; ZCC_Identifier *Sealed; VersionInfo Version; + FString *DeprecationMessage; }; struct StateOpts { @@ -232,6 +233,7 @@ class_head(X) ::= EXTEND CLASS(T) IDENTIFIER(A). head->Version = {0, 0}; head->Type = nullptr; head->Symbol = nullptr; + head->DeprecationMessage = nullptr; X = head; } @@ -247,6 +249,7 @@ class_head(X) ::= CLASS(T) IDENTIFIER(A) class_ancestry(B) class_flags(C). head->Version = C.Version; head->Type = nullptr; head->Symbol = nullptr; + head->DeprecationMessage = C.DeprecationMessage; X = head; } @@ -255,15 +258,24 @@ class_ancestry(X) ::= . { X = NULL; } class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ } %type class_flags{ClassFlagsBlock} -class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; X.Version = {0,0}; X.Sealed = NULL; } -class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; } -class_flags(X) ::= class_flags(A) FINAL. { X.Flags = A.Flags | ZCC_Final; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed;} -class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; } -class_flags(X) ::= class_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; } -class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; } -class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; X.Version = A.Version; X.Sealed = A.Sealed; } -class_flags(X) ::= class_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Replaces = A.Replaces; X.Version = C.String->GetChars(); X.Sealed = A.Sealed; } -class_flags(X) ::= class_flags(A) SEALED LPAREN states_opt(B) RPAREN. { X.Flags = A.Flags | ZCC_Sealed; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = B; } +class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; X.Version = {0,0}; X.Sealed = NULL; X.DeprecationMessage = NULL; } +class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; X.DeprecationMessage = A.DeprecationMessage; } +class_flags(X) ::= class_flags(A) FINAL. { X.Flags = A.Flags | ZCC_Final; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; X.DeprecationMessage = A.DeprecationMessage; } +class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; X.DeprecationMessage = A.DeprecationMessage; } +class_flags(X) ::= class_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; X.DeprecationMessage = A.DeprecationMessage; } +class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; X.DeprecationMessage = A.DeprecationMessage; } +class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; X.Version = A.Version; X.Sealed = A.Sealed; X.DeprecationMessage = A.DeprecationMessage; } +class_flags(X) ::= class_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Replaces = A.Replaces; X.Version = C.String->GetChars(); X.Sealed = A.Sealed; X.DeprecationMessage = A.DeprecationMessage; } +class_flags(X) ::= class_flags(A) SEALED LPAREN states_opt(B) RPAREN. { X.Flags = A.Flags | ZCC_Sealed; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = B; X.DeprecationMessage = A.DeprecationMessage; } + +class_flags(X) ::= class_flags(A) DEPRECATED LPAREN STRCONST(C) opt_deprecation_message(D) RPAREN. +{ + X.Flags = A.Flags | ZCC_Deprecated; + X.Replaces = A.Replaces; + X.Version = C.String->GetChars(); + X.Sealed = A.Sealed; + X.DeprecationMessage = D.String; +} /*----- Dottable Identifier -----*/ // This can be either a single identifier or two identifiers connected by a . @@ -412,6 +424,7 @@ struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body def->Symbol = nullptr; def->Version = S.Version; def->Flags = S.Flags; + def->DeprecationMessage = S.DeprecationMessage; X = def; } @@ -422,18 +435,27 @@ struct_def(X) ::= EXTEND STRUCT(T) IDENTIFIER(A) LBRACE opt_struct_body(B) RBRAC def->Body = B; def->Type = nullptr; def->Symbol = nullptr; + def->Version = {0, 0}; def->Flags = ZCC_Extension; + def->DeprecationMessage = nullptr; X = def; } %type struct_flags{ClassFlagsBlock} -struct_flags(X) ::= . { X.Flags = 0; X.Version = {0, 0}; } -struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; } -struct_flags(X) ::= struct_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; } -struct_flags(X) ::= struct_flags(A) CLEARSCOPE. { X.Flags = A.Flags | ZCC_ClearScope; } -struct_flags(X) ::= struct_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; } -struct_flags(X) ::= struct_flags(A) INTERNAL. { X.Flags = A.Flags | ZCC_Internal; } -struct_flags(X) ::= struct_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Version = C.String->GetChars(); } +struct_flags(X) ::= . { X.Flags = 0; X.Version = {0, 0}; X.DeprecationMessage = NULL; } +struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Version = A.Version; X.DeprecationMessage = A.DeprecationMessage; } +struct_flags(X) ::= struct_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Version = A.Version; X.DeprecationMessage = A.DeprecationMessage; } +struct_flags(X) ::= struct_flags(A) CLEARSCOPE. { X.Flags = A.Flags | ZCC_ClearScope; X.Version = A.Version; X.DeprecationMessage = A.DeprecationMessage; } +struct_flags(X) ::= struct_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Version = A.Version; X.DeprecationMessage = A.DeprecationMessage; } +struct_flags(X) ::= struct_flags(A) INTERNAL. { X.Flags = A.Flags | ZCC_Internal; X.Version = A.Version; X.DeprecationMessage = A.DeprecationMessage; } +struct_flags(X) ::= struct_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Version = C.String->GetChars(); X.DeprecationMessage = A.DeprecationMessage; } + +struct_flags(X) ::= struct_flags(A) DEPRECATED LPAREN STRCONST(C) opt_deprecation_message(D) RPAREN. +{ + X.Flags = A.Flags | ZCC_Deprecated; + X.Version = C.String->GetChars(); + X.DeprecationMessage = D.String; +} opt_struct_body(X) ::= . { X = NULL; } opt_struct_body(X) ::= struct_body(X). diff --git a/src/common/scripting/frontend/zcc_compile.cpp b/src/common/scripting/frontend/zcc_compile.cpp index dfe9dfa13..fb49cf39a 100644 --- a/src/common/scripting/frontend/zcc_compile.cpp +++ b/src/common/scripting/frontend/zcc_compile.cpp @@ -751,6 +751,13 @@ void ZCCCompiler::CreateStructTypes() s->strct->Type->mVersion = s->strct->Version; } + if (s->strct->Flags & ZCC_Deprecated) + { + s->strct->Type->mVersion = s->strct->Version; + s->strct->Type->TypeDeprecated = true; + s->strct->Type->mDeprecationMessage = s->strct->DeprecationMessage ? *s->strct->DeprecationMessage : ""; + } + if (s->strct->Flags & ZCC_Internal) { s->strct->Type->TypeInternal = true; @@ -919,6 +926,13 @@ void ZCCCompiler::CreateClassTypes() { c->Type()->mVersion = c->cls->Version; } + + if (c->cls->Flags & ZCC_Deprecated) + { + c->Type()->mVersion = c->cls->Version; + c->Type()->TypeDeprecated = true; + c->Type()->mDeprecationMessage = c->cls->DeprecationMessage ? *c->cls->DeprecationMessage : ""; + } if (c->cls->Flags & ZCC_Final) @@ -2177,7 +2191,16 @@ PType *ZCCCompiler::ResolveUserType(PType *outertype, ZCC_BasicType *type, ZCC_I if (sym != nullptr && sym->IsKindOf(RUNTIME_CLASS(PSymbolType))) { auto ptype = static_cast(sym)->Type; - if (ptype->mVersion > mVersion) + + if (ptype->TypeDeprecated) + { + if(ptype->mVersion <= mVersion && !outertype->TypeDeprecated && fileSystem.GetFileContainer(Lump) > 0) + { + Warn(type, "Type %s is deprecated since ZScript version %d.%d.%d%s%s", + FName(type->UserType->Id).GetChars(), mVersion.major, mVersion.minor, mVersion.revision, ptype->mDeprecationMessage.IsEmpty() ? "" : ": ", ptype->mDeprecationMessage.GetChars()); + } + } + else if (ptype->mVersion > mVersion) { Error(type, "Type %s not accessible to ZScript version %d.%d.%d", FName(type->UserType->Id).GetChars(), mVersion.major, mVersion.minor, mVersion.revision); return TypeError; diff --git a/src/common/scripting/frontend/zcc_parser.h b/src/common/scripting/frontend/zcc_parser.h index 47ab2d65f..d12d7b87e 100644 --- a/src/common/scripting/frontend/zcc_parser.h +++ b/src/common/scripting/frontend/zcc_parser.h @@ -243,6 +243,7 @@ struct ZCC_Struct : ZCC_NamedNode ZCC_TreeNode *Body; PContainerType *Type; VersionInfo Version; + FString *DeprecationMessage; }; struct ZCC_Property : ZCC_NamedNode diff --git a/wadsrc/static/zscript/engine/dictionary.zs b/wadsrc/static/zscript/engine/dictionary.zs index 0bd48cd69..6c778492f 100644 --- a/wadsrc/static/zscript/engine/dictionary.zs +++ b/wadsrc/static/zscript/engine/dictionary.zs @@ -5,7 +5,7 @@ * * @note keys are case-sensitive. */ -class Dictionary +class Dictionary deprecated("4.15", "Use Map instead") { native static Dictionary Create(); @@ -38,7 +38,7 @@ class Dictionary * DictionaryIterator is not serializable. To make DictionaryIterator a class * member, use `transient` keyword. */ -class DictionaryIterator +class DictionaryIterator deprecated("4.15", "Use Map instead") { native static DictionaryIterator Create(Dictionary dict);