// Copyright Epic Games, Inc. All Rights Reserved. // uLang Compiler Public API #pragma once #include "uLang/Common/Containers/SharedPointer.h" #include "uLang/Common/Containers/SharedPointerArray.h" #include "uLang/Common/Templates/Conditionals.h" #include "uLang/Common/Text/Symbol.h" #include "uLang/Common/Text/UTF8String.h" #include "uLang/Semantics/DataDefinition.h" #include "uLang/Semantics/SemanticTypes.h" #include "uLang/Semantics/Signature.h" #include "uLang/SourceProject/PackageRole.h" #include "uLang/Syntax/VstNode.h" #define UE_API VERSECOMPILER_API namespace uLang { // Forward declarations class CEnumeration; class CAstNode; class CAstCompilationUnit; class CExpressionBase; class CExprCodeBlock; class CExprIdentifierBase; class CSemanticProgram; class CSnippet; class CModule; class CModulePart; class CModuleAlias; class CControlScope; class CTypeAlias; class CTypeVariable; class CEnumerator; // NOTE: (YiLiangSiew) Currently, Visual Verse relies on the numerical values of these enumerations. If you // change this, be sure to update `BaseVisualVerseSettings.ini` as well. #define VERSE_VISIT_AST_NODE_TYPES(v) \ /* Helper expressions */ \ v(Error_, CExprError) /* An expression that could not be analyzed due to an error. Attempts to analyze should propagate the error. */ \ v(Placeholder_, CExprPlaceholder) /* Corresponds to a Placeholder Vst node. */ \ v(External, CExprExternal) /* An (unknown) external expression - should never reach the code generator */ \ v(PathPlusSymbol, CExprPathPlusSymbol) /* Path of the current scope plus a symbol - /unrealengine.com/UnrealEngine@5 */ \ \ /* Literals */ \ v(Literal_Logic, CExprLogic) /* Logic literal - true/false */ \ v(Literal_Number, CExprNumber) /* Integer literal - 42, 0, -123, 123_456_789, 0x12fe, 0b101010 */ \ /* or Float literal - 42.0, 0.0, -123.0, 123_456.0, 3.14159, .5, -.33, 4.2e1, -1e6, 7.5e-8 */ \ v(Literal_Char, CExprChar) /* Character literal - 'a', '\n' */ \ v(Literal_String, CExprString) /* String literal - "Hello, world!", "Line 1\nLine2" */ \ v(Literal_Path, CExprPath) /* Path literal - /unrealengine.com/UnrealEngine@5 */ \ v(Literal_Enum, CExprEnumLiteral) /* Enumerator - Color.Red, Size.XXL */ \ v(Literal_Type, CExprType) /* Type - type{} */ \ v(Literal_Function, CExprFunctionLiteral) /* a=>b or function(a){b} */ \ \ /* Identifiers */ \ v(Identifier_Unresolved, CExprIdentifierUnresolved) /* An existing identifier that is unresolved. It is produced by desugaring and consumed by analysis. */ \ v(Identifier_Class, CExprIdentifierClass) /* Type identifier - e.g. my_type, int, color, string */ \ v(Identifier_Module, CExprIdentifierModule) /* Module name */ \ v(Identifier_ModuleAlias, CExprIdentifierModuleAlias) /* Module alias name */ \ v(Identifier_Enum, CExprEnumerationType) /* Enum name */ \ v(Identifier_Interface, CExprInterfaceType) /* Interface name */ \ v(Identifier_Data, CExprIdentifierData) /* Scoped data-definition (class member, local, etc.) */ \ v(Identifier_TypeAlias, CExprIdentifierTypeAlias) /* Access to type alias */ \ v(Identifier_TypeVariable, CExprIdentifierTypeVariable) /* Access to a type variable */ \ v(Identifier_Function, CExprIdentifierFunction) /* Access to functions */ \ v(Identifier_OverloadedFunction, CExprIdentifierOverloadedFunction) /* An overloaded function identifier that hasn't been resolved to a specific overload. */ \ v(Identifier_Self, CExprSelf) /* Access to the instance the current function is being invoked on. */ \ v(Identifier_Local, CExprLocal) /* Access to the the current scope for the identifier that has this qualifier. */ \ v(Identifier_BuiltInMacro, CExprIdentifierBuiltInMacro) /* An intrinsic macro: option{}, logic{}, array{}, etc. */ \ \ /* Multi purpose syntax */ \ v(Definition, CExprDefinition) /* represents syntactic forms elt:domain=value, elt:domain, elt=value */ \ \ /* Macro */ \ v(MacroCall, CExprMacroCall) /* macro invocations should be resolved by the compiler; the code generator should never encounter this. */ \ \ /* Invocations */ \ v(Invoke_Invocation, CExprInvocation) /* Routine call - expr1.call(expr2, expr3) */ \ v(Invoke_UnaryArithmetic, CExprUnaryArithmetic) /* negation */ \ v(Invoke_BinaryArithmetic, CExprBinaryArithmetic) /* add, sub, mul, div; two operands only */ \ v(Invoke_ShortCircuitAnd, CExprShortCircuitAnd) /* short-circuit evaluation of logic and */ \ v(Invoke_ShortCircuitOr, CExprShortCircuitOr) /* short-circuit evaluation of logic or */ \ v(Invoke_LogicalNot, CExprLogicalNot) /* logical not operator */ \ v(Invoke_Comparison, CExprComparison) /* comparison operators */ \ v(Invoke_QueryValue, CExprQueryValue) /* Querying the value of a logic or option. */ \ v(Invoke_MakeOption, CExprMakeOption) /* Making an option value. */ \ v(Invoke_MakeArray, CExprMakeArray) /* Making an array value. */ \ v(Invoke_MakeMap, CExprMakeMap) /* Making a map value. */ \ v(Invoke_MakeTuple, CExprMakeTuple) /* Making a tuple value - (1, 2.0f, "three") */ \ v(Invoke_TupleElement, CExprTupleElement) /* Tuple element access `TupleExpr(Idx)` */ \ v(Invoke_MakeRange, CExprMakeRange) /* Making a range value. */ \ v(Invoke_Type, CExprInvokeType) /* Invoke a type as a function on a value - type(expr) or type[expr]. */ \ v(Invoke_PointerToReference, CExprPointerToReference) /* Access the mutable reference behind the pointer. */ \ v(Invoke_Set, CExprSet) /* Evaluate operand to an l-expression. */ \ v(Invoke_NewPointer, CExprNewPointer) /* Create a new pointer from an initial value. */ \ v(Invoke_ReferenceToValue, CExprReferenceToValue) /* Evaluates the value of an expression yielding a reference type. */ \ \ v(Assignment, CExprAssignment) /* Assignment operation - expr1 = expr2, expr1 := expr2, expr1 += expr2, etc. */ \ \ /* TypeFormers */ \ v(Invoke_ArrayFormer, CExprArrayTypeFormer) /* Invoke (at compile time) a formation of an array of another type */ \ v(Invoke_GeneratorFormer, CExprGeneratorTypeFormer) /* Invoke (at compile time) a formation of an generator type. */ \ v(Invoke_MapFormer, CExprMapTypeFormer) /* Invoke (at compile time) a formation of a map from a key and value type. */ \ v(Invoke_OptionFormer, CExprOptionTypeFormer) /* Invoke (at compile time) a formation of an option of some primitive type */ \ v(Invoke_Subtype, CExprSubtype) /* Invoke (at compile time) a formation of a metaclass type. */ \ v(Invoke_TupleType, CExprTupleType) /* Get or create a tuple based on `tuple(type1, type2, ...)` */ \ v(Invoke_Arrow, CExprArrow) /* Create a function type from a parameter and return type. */ \ \ v(Invoke_ArchetypeInstantiation, CExprArchetypeInstantiation) /* Initializer list style instantiation - Type{expr1, id=expr2, ...} */ \ \ /* Flow Control */ \ v(Flow_CodeBlock, CExprCodeBlock) /* Code block - block {expr1; expr2} */ \ v(Flow_Let, CExprLet) /* let {definition1; definition2} */ \ v(Flow_Defer, CExprDefer) /* defer {expr1; expr2} */ \ v(Flow_If, CExprIf) /* Conditional with failable tests- if (Test[]) {clause1}, if (Test[]) {clause1} else {else_clause} */ \ v(Flow_Iteration, CExprIteration) /* Bounded iteration over an iterable type - for(Num:Nums, ...) {DoStuff(Num, ...)} */ \ v(Flow_Loop, CExprLoop) /* Simple loop - loop {DoStuff()} */ \ v(Flow_Break, CExprBreak) /* Control flow early exit - loop {if (IsEarlyExit[]) {break}; DoLoopStuff()} */ \ v(Flow_Return, CExprReturn) /* Return statement - return expr */ \ v(Flow_ProfileBlock, CExprProfileBlock) /* profile block */\ \ v(Ir_For, CIrFor) /* Bounded iteration over an iterable type - for(Num:Nums) {DoStuff(Num)} */ \ v(Ir_ForBody, CIrForBody) /* Only used inside CIrFor, and only around the body of the innermost CIrFor */ \ v(Ir_ArrayAdd, CIrArrayAdd) /* Append an item to an array */ \ v(Ir_MapAdd, CIrMapAdd) /* Add a key-value pair to a map */ \ v(Ir_ArrayUnsafeCall, CIrArrayUnsafeCall) /* Infallibly access an element of an array */ \ v(Ir_ConvertToDynamic, CIrConvertToDynamic) /* Converts a value to a dynamically typed value. */ \ v(Ir_ConvertFromDynamic, CIrConvertFromDynamic) /* Converts a value from a dynamically typed value. */ \ \ /* Concurrency Primitives */ \ v(Concurrent_Sync, CExprSync) /* sync {Coro1(); Coro2()} */ \ v(Concurrent_Rush, CExprRush) /* rush {Coro1(); Coro2()} */ \ v(Concurrent_Race, CExprRace) /* race {Coro1(); Coro2()} */ \ v(Concurrent_SyncIterated, CExprSyncIterated) /* sync(Item:Container) {Item.Coro1(); Coro2(Item)} */ \ v(Concurrent_RushIterated, CExprRushIterated) /* rush(Item:Container) {Item.Coro1(); Coro2(Item)} */ \ v(Concurrent_RaceIterated, CExprRaceIterated) /* race(Item:Container) {Item.Coro1(); Coro2(Item)} */ \ v(Concurrent_Branch, CExprBranch) /* branch {Coro1(); Coro2()} */ \ v(Concurrent_Spawn, CExprSpawn) /* spawn {Coro()} */ \ \ /* Definitions */ \ v(Definition_Module, CExprModuleDefinition) \ v(Definition_Enum, CExprEnumDefinition) \ v(Definition_Interface, CExprInterfaceDefinition) \ v(Definition_Class, CExprClassDefinition) \ v(Definition_Data, CExprDataDefinition) \ v(Definition_IterationPair, CExprIterationPairDefinition) \ v(Definition_Function, CExprFunctionDefinition) \ v(Definition_TypeAlias, CExprTypeAliasDefinition) \ v(Definition_Using, CExprUsing) \ v(Definition_Import, CExprImport) \ v(Definition_Where, CExprWhere) \ v(Definition_Var, CExprVar) \ v(Definition_ScopedAccessLevel, CExprScopedAccessLevelDefinition) \ v(Invoke_MakeNamed, CExprMakeNamed) /* ?ParamName:=Value pair - or default value placeholder (value not set) */ \ \ /* Containing Context - may contain expressions though they aren't expressions themselves */ \ v(Context_Project, CAstProject) \ v(Context_CompilationUnit, CAstCompilationUnit) \ v(Context_Package, CAstPackage) \ v(Context_Snippet, CExprSnippet) #define FORWARD_DECLARE(_, Class) class Class; VERSE_VISIT_AST_NODE_TYPES(FORWARD_DECLARE) #undef FORWARD_DECLARE /** * This is used to differentiate between different types of AST nodes when it is only * known that an instance is of type CAstNode, but not the specific subclass. * It is returned by the method CAstNode::GetNodeType() **/ enum class EAstNodeType : uint8_t { #define VISIT_AST_NODE_TYPE(Name, Class) Name, VERSE_VISIT_AST_NODE_TYPES(VISIT_AST_NODE_TYPE) #undef VISIT_AST_NODE_TYPE }; struct SAstNodeTypeInfo { const char* _EnumeratorName; const char* _CppClassName; }; /** Returns the name of an AST node type. */ VERSECOMPILER_API SAstNodeTypeInfo GetAstNodeTypeInfo(uLang::EAstNodeType Type); //--------------------------------------------------------------------------------------- // Indicates whether an expression should return immediately - such as functions, after a // duration (including immediately) such as coroutines or either. enum class EInvokeTime : uint8_t { Immediate = 1 << 0, // May only call an immediate expression (such as a function call) and any async expression (such as a coroutine call) should result in an error. Async = 1 << 1, // May only call an async expression (such as a coroutine call) and any immediate expression (such as a function call) should result in an error. Only true within one of the pre-defined calling contexts (`sync{}`, `race{}`, etc.). Any_ = Immediate | Async // Calling either immediate or async expressions is allowed. }; inline const char* InvokeTimeAsCString(EInvokeTime InvokeTime) { switch(InvokeTime) { case EInvokeTime::Immediate: return "Immediate"; case EInvokeTime::Async: return "Async"; case EInvokeTime::Any_: return "Any_"; default: ULANG_UNREACHABLE(); } } /** * Abstract base for applying some operation / iterating through AST structures. * @see CAstNode::VisitChildren() **/ struct SAstVisitor { /** Called when visiting an AST node **/ virtual void VisitImmediate(const char* FieldName, CUTF8StringView Value) {} virtual void VisitImmediate(const char* FieldName, int64_t Value) {} virtual void VisitImmediate(const char* FieldName, double Value) {} virtual void VisitImmediate(const char* FieldName, bool Value) {} virtual void VisitImmediate(const char* FieldName, const CTypeBase* Type) {} virtual void VisitImmediate(const char* FieldName, const CDefinition& Definition) {} virtual void VisitImmediate(const char* FieldName, const Verse::Vst::Node& VstNode) {} void VisitImmediate(const char* FieldName, const char* CString) { VisitImmediate(FieldName, CUTF8StringView(CString)); } virtual void Visit(const char* FieldName, CAstNode& AstNode) = 0; virtual void BeginArray(const char* FieldName, intptr_t Num) {} virtual void VisitElement(CAstNode& AstNode) = 0; virtual void EndArray() {} template void Visit(const char* FieldName, const TSPtrG& NodePointer) { if (!bAllowNull || NodePointer.IsValid()) { Visit(FieldName, *NodePointer); } } template void VisitElement(const TSPtrG& NodePointer) { if (!bAllowNull || NodePointer.IsValid()) { VisitElement(*NodePointer); } } template void VisitArray(const char* FieldName, const TArrayG, ElementAllocatorType, ElementAllocatorArgTypes...>& Array) { BeginArray(FieldName, Array.Num()); for (const TSPtrG& Element : Array) { if (!bAllowNull || Element.IsValid()) { VisitElement(*Element); } } EndArray(); } template void VisitArray(const char* FieldName, const TSPtrArrayG& Array) { BeginArray(FieldName, Array.Num()); for (NodeType* Node : Array) { if (!bAllowNull || Node) { VisitElement(*Node); } } EndArray(); } }; enum class EVstMappingType { Ast, AstNonReciprocal, Ir }; /** * Abstract base class for AST nodes. */ class CAstNode : public CSharedMix { public: CAstNode(EVstMappingType VstMappingType = EVstMappingType::Ast) : _VstMappingType(VstMappingType) {} UE_API virtual ~CAstNode(); virtual EAstNodeType GetNodeType() const = 0; virtual const CExpressionBase* AsExpression() const { return nullptr; } virtual CExpressionBase* AsExpression() { return nullptr; } virtual bool MayHaveAttributes() const { return true; } virtual const CExprIdentifierBase* AsIdentifierBase() const { return nullptr; } /** * Iterates over this AST node's immediate fields, calling Visitor.VisitImmediate for each immediate field. */ virtual void VisitImmediates(SAstVisitor& Visitor) const { const char* VstMappingTypeString; switch(_VstMappingType) { case EVstMappingType::Ast: VstMappingTypeString = "Ast"; break; case EVstMappingType::AstNonReciprocal: VstMappingTypeString = "AstNonReciprocal"; break; case EVstMappingType::Ir: VstMappingTypeString = "Ir"; break; default: ULANG_UNREACHABLE(); }; Visitor.VisitImmediate("VstMappingType", VstMappingTypeString); if (_MappedVstNode) { Visitor.VisitImmediate("MappedVstNode", *_MappedVstNode); } } /** * Iterates over this AST node's direct children, calling Visitor.Visit for each child. **/ virtual void VisitChildren(SAstVisitor& Visitor) const {} /** * Wrapper for VisitChildren that takes a lambda that is called for each child. * The signature for the lambda should be (const SAstVisitor& RecurseVisitor, CAstNode&) * You can use RecurseVisitor to recursively call VisitChildren with the same lambda. */ template ULANG_FORCEINLINE void VisitChildrenLambda(FunctionType&& Function) const; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Text methods virtual CUTF8String GetErrorDesc() const = 0; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Syntax <> Semantic Mapping const Verse::Vst::Node* GetMappedVstNode() const { return _MappedVstNode; } void SetNonReciprocalMappedVstNode(const Verse::Vst::Node* VstNode) { _VstMappingType = EVstMappingType::AstNonReciprocal; _MappedVstNode = VstNode; } void SetIrMappedVstNode(const Verse::Vst::Node* VstNode) { _VstMappingType = EVstMappingType::Ir; _MappedVstNode = VstNode; } // True if this AstNode is used to represent an IrNode. Needed to disable some asserts and clean up code. // Will be removed when IrNodes have their own type. bool IsIrNode() const { return _VstMappingType == EVstMappingType::Ir; } bool IsVstMappingReciprocal() const { return _VstMappingType == EVstMappingType::Ast; } protected: friend struct Verse::Vst::Node; // Syntax<>Semantic mapping mutable EVstMappingType _VstMappingType; mutable const Verse::Vst::Node* _MappedVstNode{nullptr}; }; /** * Abstract base class for AST expressions. * Created from abstract syntax tree (CExpressionBase and CSemanticProgram) generated by semantic analyzer. **/ class CExpressionBase : public CAstNode, public CAttributable { public: explicit CExpressionBase(EVstMappingType VstMappingType = EVstMappingType::Ast) : CAstNode(VstMappingType) {} explicit CExpressionBase(const CTypeBase* InResultType) : _Report(SAnalysisResult{InResultType}) {} virtual const CExpressionBase* AsExpression() const override { return this; } virtual CExpressionBase* AsExpression() override { return this; } virtual bool MayHaveAttributes() const override { return false; } using TMacroSymbols = TArrayG>; // True if this expression can be path of a segment. It works at all times, i.e., also before macro expression have been processed. // The MacroSymbols argument is used in the latter case. virtual bool CanBePathSegment(const TMacroSymbols& MacroSymbols) const { return false; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CAstNode::VisitImmediates(Visitor); if (_Report.IsSet()) { Visitor.VisitImmediate("ResultType", _Report->ResultType); } } // Determines if expression is immediate (completes within the current update/frame) or // async (completes within the current update/frame or later). // // @TODO: SOL-1423, DetermineInvokeTime() re-traverses the expression tree, which could add up time wise // (approaching on n^2) -- there should be a beter way to check this on the initial ProcessExpression() EInvokeTime DetermineInvokeTime(const CSemanticProgram& Program) const { return (FindFirstAsyncSubExpr(Program) == nullptr) ? EInvokeTime::Immediate : EInvokeTime::Async; } // Returns itself or the first async sub-expression or return nullptr if it and all its sub-expressions are immediate. virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const { return nullptr; } // Returns whether the expression may fail. virtual bool CanFail(const CAstPackage* Package) const { return false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Type methods UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const; UE_API void SetResultType(const CTypeBase* InResultType); UE_API void RefineResultType(const CTypeBase* RefinedResultType); bool IsAnalyzed() const { return _Report.IsSet(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Comparison static bool BaseCompare(const CExpressionBase& A, const CExpressionBase& B) { return A.GetNodeType() == B.GetNodeType() && A._Report == B._Report; } virtual bool operator==(const CExpressionBase& Other) const = 0; bool operator!=(const CExpressionBase& Other) const { return !(*this == Other); } static bool IsSubExprEqual(const CExpressionBase* Lhs, const CExpressionBase* Rhs); static bool IsSubExprEqual(const CExpressionBase& Lhs, const CExpressionBase& Rhs); static bool IsSubExprEqual(const TSPtr& Lhs, const TSPtr& Rhs); static bool IsSubExprEqual(const TSRef& Lhs, const TSRef& Rhs); static bool AreSubExprsEqual(const TSPtrArray& Lhs, const TSPtrArray& Rhs); static bool AreSubExprsEqual(const TArray>& Lhs, const TArray>& Rhs); const CTypeBase* IrGetResultType() const { return _Report ? _Report->ResultType : nullptr; } void IrSetResultType(const CTypeBase* TypeBase) { if (TypeBase) { _Report = SAnalysisResult{ TypeBase }; } else { _Report.Reset(); } } protected: // Analysis Results struct SAnalysisResult { bool operator==(const SAnalysisResult& Other) const { return ResultType == Other.ResultType; } /** The type to which this node evaluates. */ const CTypeBase* ResultType; }; TOptional _Report; }; // CExpressionBase /** * Base for expressions that have an array of subexpressions * Subclasses: CExprMakeArray, CExprMakeMap, CExprMakeTuple, CExprCodeBlock, CExprConcurrentBlockBase */ class CExprCompoundBase : public CExpressionBase { public: // Methods explicit CExprCompoundBase(int32_t ReserveSubExprNum, EVstMappingType VstMappingType = EVstMappingType::Ast) : CExpressionBase(VstMappingType) { _SubExprs.Reserve(ReserveSubExprNum); } explicit CExprCompoundBase(TSPtrArray&& SubExprs, EVstMappingType VstMappingType = EVstMappingType::Ast) : CExpressionBase(VstMappingType) , _SubExprs(Move(SubExprs)) {} explicit CExprCompoundBase(TSPtr&& SubExpr1, TSPtr&& SubExpr2) : CExprCompoundBase(2) { AppendSubExpr(Move(SubExpr1)); AppendSubExpr(Move(SubExpr2)); } CExprCompoundBase() = default; bool IsEmpty() const { return _SubExprs.IsEmpty(); } int32_t SubExprNum() const { return _SubExprs.Num(); } const uLang::CExpressionBase* GetLastSubExpr() const { return _SubExprs.Last(); } const TSPtrArray& GetSubExprs() const { return _SubExprs; } TSPtrArray& GetSubExprs() { return _SubExprs; } TSPtrArray&& TakeSubExprs() { return Move(_SubExprs); } void AppendSubExpr(TSPtr SubExpr) { _SubExprs.Add(Move(SubExpr)); } void PrependSubExpr(TSPtr SubExpr) { _SubExprs.Insert(Move(SubExpr), 0); } void SetSubExprs(TSPtrArray AnalyzedExprs) { _SubExprs = Move(AnalyzedExprs); } void ReplaceSubExpr(TSPtr SubExpr, int32_t Index) { ULANG_ASSERTF(Index >= 0 && Index < _SubExprs.Num(), "Replacing invalid subexpression index"); _SubExprs.ReplaceAt(Move(SubExpr), Index); } UE_API virtual bool CanFail(const CAstPackage* Package) const override; UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.VisitArray("SubExprs", _SubExprs); } UE_API virtual bool operator==(const CExpressionBase& Other) const override; protected: // Internal data TSPtrArray _SubExprs; }; /** * Base class of binary operators. */ class CExprBinaryOp : public CExpressionBase { private: // Operands TSPtr _Lhs; TSPtr _Rhs; public: const TSPtr& Lhs() const { return _Lhs; } const TSPtr& Rhs() const { return _Rhs; } TSPtr&& TakeRhs() { return Move(_Rhs); } void SetLhs(TSPtr&& NewLhs) { _Lhs = Move(NewLhs); } void SetRhs(TSPtr&& NewRhs) { _Rhs = Move(NewRhs); } CExprBinaryOp(TSPtr&& Lhs, TSPtr&& Rhs) : _Lhs(Move(Lhs)), _Rhs(Move(Rhs)) {} virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { const CExpressionBase* AsyncExpr = _Lhs->FindFirstAsyncSubExpr(Program); return AsyncExpr ? AsyncExpr : _Rhs->FindFirstAsyncSubExpr(Program); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Lhs", _Lhs); Visitor.Visit("Rhs", _Rhs); } }; // Note - if these subclasses of CExpressionBase become more sophisticated they should // probably be moved to their own files. /** * Expression for external{} macro used in digests - should never reach the code generator */ class CExprExternal : public CExpressionBase { public: // Methods UE_API explicit CExprExternal(const CSemanticProgram& Program); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::External; } virtual CUTF8String GetErrorDesc() const override { return "external{}"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::External; } }; /** * Logic literal - true/false **/ class CExprLogic : public CExpressionBase { public: bool _Value; UE_API explicit CExprLogic(const CSemanticProgram& Program, bool Value); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Literal_Logic; } virtual CUTF8String GetErrorDesc() const override { return _Value ? "true" : "false"; } virtual bool operator==(const CExpressionBase& Other) const override { return BaseCompare(*this, Other) && _Value == static_cast(Other)._Value; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Value", _Value); } }; /** * Integer literal - 42, 0, -123, 123_456_789, 0x12fe, 0b101010 */ class CExprNumber : public CExpressionBase { private: union { Integer _IntValue; Float _FloatValue; }; bool _bIsFloat : 1; public: ULANG_FORCEINLINE explicit CExprNumber() : _IntValue(0) , _bIsFloat(false) {} UE_API explicit CExprNumber(CSemanticProgram&, Integer); UE_API explicit CExprNumber(CSemanticProgram&, Float); bool IsFloat() const { return _bIsFloat; } Integer GetIntValue() const { ULANG_ASSERTF(!_bIsFloat, "Float number being treated as integer."); return _IntValue; } UE_API void SetIntValue(CSemanticProgram&, Integer); Float GetFloatValue() const { ULANG_ASSERTF( _bIsFloat, "Int number being treated as float"); return _FloatValue; } UE_API void SetFloatValue(CSemanticProgram&, Float); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Literal_Number; } virtual CUTF8String GetErrorDesc() const override { return _bIsFloat ? "float literal" : "integer literal"; } virtual bool operator==(const CExpressionBase& Other) const override { return BaseCompare(*this, Other) && this->_IntValue == static_cast(Other)._IntValue; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); if (_bIsFloat) { Visitor.VisitImmediate("FloatValue", _FloatValue); } else { Visitor.VisitImmediate("IntValue", _IntValue); } } }; /** * Character literal - 'H' '\n' '{0o00}' '{0u1f600}' **/ class CExprChar : public CExpressionBase { public: enum class EType { UTF8CodeUnit, UnicodeCodePoint }; uint32_t _CodePoint; EType _Type; explicit CExprChar(uint32_t CodePoint, EType Type) : _CodePoint(CodePoint) , _Type(Type) { if(_Type == EType::UTF8CodeUnit) { ULANG_ASSERTF(_CodePoint <= 0xFF, "utf8 code units must be <= 0xFF"); } } CUTF8String AsString() const { switch (_Type) { case CExprChar::EType::UTF8CodeUnit: { const UTF8Char CodeUnit = {static_cast(_CodePoint)}; return CUTF8StringView(&CodeUnit, &CodeUnit + 1); } case CExprChar::EType::UnicodeCodePoint: { SUTF8CodePoint Utf8 = CUnicode::EncodeUTF8(_CodePoint); return CUTF8StringView(Utf8.Units, Utf8.Units + Utf8.NumUnits); } default: ULANG_UNREACHABLE(); } } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Literal_Char; } virtual CUTF8String GetErrorDesc() const override { return "char literal"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Literal_Char && _CodePoint == static_cast(Other)._CodePoint; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("CodePoint", static_cast(_CodePoint)); } }; /** * String literal - "Hello, world!", "Line 1\nLine2" **/ class CExprString : public CExpressionBase { public: // Note that in the future this (or a related class) may include an array of substrings and sub-expressions for string interpolation. // Ready to use string with any escaped characters translated CUTF8String _String; explicit CExprString(CUTF8String String) : _String(Move(String)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Literal_String; } virtual CUTF8String GetErrorDesc() const override { return "string literal"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Literal_String && _String == static_cast(Other)._String; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("String", _String); } }; /** * Path literal - /unrealengine.com/UnrealEngine **/ class CExprPath : public CExpressionBase { public: // Ready to use path with any escaped characters translated CUTF8String _Path; explicit CExprPath(CUTF8String Path) : _Path(Move(Path)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Literal_Path; } virtual CUTF8String GetErrorDesc() const override { return "path literal"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Literal_Path && _Path == static_cast(Other)._Path; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Path", _Path); } }; /** * Expression that evaluates to the path of the current scope, plus a given symbol, semantic analysis replaces this node with a CExprString **/ class CExprPathPlusSymbol : public CExpressionBase { public: const CSymbol _Symbol; explicit CExprPathPlusSymbol(const CSymbol& Symbol) : _Symbol(Symbol) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::PathPlusSymbol; } virtual CUTF8String GetErrorDesc() const override { return "path plus symbol"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::PathPlusSymbol && _Symbol == static_cast(Other)._Symbol; } }; /** * The base class of identifiers expressions. */ class CExprIdentifierBase : public CExpressionBase { public: CExprIdentifierBase(TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr) : _Context(Move(Context)), _Qualifier(Move(Qualifier)) {} const TSPtr& Context() const { return _Context; } const TSPtr& Qualifier() const { return _Qualifier; } TSPtr&& TakeContext() { return Move(_Context); } TSPtr&& TakeQualifier() { return Move(_Qualifier); } void SetContext(TSPtr Context) { _Context = Move(Context); } void SetQualifier(TSPtr Qualifier) { _Qualifier = Move(Qualifier); } // CAstNode interface. virtual bool MayHaveAttributes() const override { return true; } // CExpressionBase interface. virtual bool CanFail(const CAstPackage* Package) const override { return _Context.IsValid() && _Context->CanFail(Package); } virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { const CExpressionBase* AsyncExpr = nullptr; if (_Context) { AsyncExpr = _Context->FindFirstAsyncSubExpr(Program); } if (!AsyncExpr && _Qualifier) { AsyncExpr = _Qualifier->FindFirstAsyncSubExpr(Program); } return AsyncExpr; } virtual bool operator==(const CExpressionBase& Other) const override { return CExpressionBase::BaseCompare(*this, Other) && _Context == static_cast(Other)._Context && _Qualifier == static_cast(Other)._Qualifier; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Context", _Context); } /// This is specifically named as such to avoid shadowing other virtual methods (such as on `CNominalType`). virtual const CDefinition* IdentifierDefinition() const { return nullptr; } /// This node contains an identifier virtual const CExprIdentifierBase* AsIdentifierBase() const override { return this; } private: TSPtr _Context; TSPtr _Qualifier; }; /** * Enumerator - #GlobalEnum.enumeration, #.enumeration, MyType#Confirm.yes, #Confirm.yes, * #.yes, MyType@set_size#size.medium, #size.medium, #.medium, #position.upper, #.upper **/ class CExprEnumLiteral : public CExprIdentifierBase { public: // Specific enumerator this represents - note that it also contains a pointer to its enumeration type. const CEnumerator* _Enumerator; ULANG_FORCEINLINE explicit CExprEnumLiteral(const CEnumerator* Enumerator, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr) : CExprIdentifierBase(Move(Context), Move(Qualifier)) , _Enumerator(Enumerator) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Literal_Enum; } virtual CUTF8String GetErrorDesc() const override { return "enumerator"; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual bool operator==(const CExpressionBase& Other) const override { return CExprIdentifierBase::operator==(Other) && _Enumerator == static_cast(Other)._Enumerator; } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; virtual const CDefinition* IdentifierDefinition() const override; }; /** * Type expression - type{} */ class CExprType : public CExpressionBase { public: const TSRef _AbstractValue; CExprType(TSRef&& AbstractValue, const CTypeType& TypeType) : _AbstractValue(Move(AbstractValue)) { SetResultType(&TypeType); } const CTypeType* GetTypeType() const { return static_cast(_Report->ResultType); } const CTypeBase* GetType() const { const CTypeType* TypeType = GetTypeType(); return TypeType->PositiveType(); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Literal_Type; } virtual CUTF8String GetErrorDesc() const override { return "type"; } virtual bool operator==(const CExpressionBase& Other) const override { return GetNodeType() == Other.GetNodeType() && IsSubExprEqual(_AbstractValue, static_cast(Other)._AbstractValue); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("AbstractValue", _AbstractValue); } }; /** * Function literal - a=>b or function(a){b} */ class CExprFunctionLiteral : public CExpressionBase { public: CExprFunctionLiteral(TSRef&& Domain, TSRef&& Range) : _Domain(Move(Domain)) , _Range(Move(Range)) {} const TSRef& Domain() const { return _Domain; } const TSRef& Range () const { return _Range ; } void SetDomain(TSRef&& NewDomain) { _Domain = Move(NewDomain); } void SetRange(TSRef&& NewRange ) { _Range = Move(NewRange) ; } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Literal_Function; } virtual CUTF8String GetErrorDesc() const override { return "function literal"; } virtual bool operator==(const CExpressionBase& Other) const override { return GetNodeType() == Other.GetNodeType() && IsSubExprEqual(_Domain, static_cast(Other)._Domain) && IsSubExprEqual(_Range, static_cast(Other)._Range); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Domain", _Domain); Visitor.Visit("Range", _Range); } private: TSRef _Domain; TSRef _Range; }; /** * Access to the instance the current function is being invoked on. **/ class CExprSelf : public CExprIdentifierBase { public: CExprSelf(const CTypeBase* Type, TSPtr&& Qualifier = nullptr) : CExprIdentifierBase(nullptr, Move(Qualifier)) { SetResultType(Type); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Self; } virtual CUTF8String GetErrorDesc() const override { return "'Self'"; } virtual const CDefinition* IdentifierDefinition() const override { // TODO: (yiliang.siew) Implement this. return nullptr; } }; /// Represents the `(local:) qualifier`. class CExprLocal : public CExprIdentifierBase { public: CExprLocal(const CScope& Scope) : CExprIdentifierBase(), _Scope(Scope) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Local; } virtual CUTF8String GetErrorDesc() const override { return "'(local:)'"; } const CScope& GetScope() const { return _Scope; } private: const CScope& _Scope; }; /** * Represents a name of a a compiler built-in macro; e.g. option, array. * These should always be resolved by analysis and never make their way into code gen. **/ class CExprIdentifierBuiltInMacro : public CExprIdentifierBase { public: const CSymbol _Symbol; CExprIdentifierBuiltInMacro(const CSymbol Symbol, const CTypeBase* Type) : CExprIdentifierBase(nullptr, nullptr) , _Symbol(Symbol) { SetResultType(Type); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_BuiltInMacro; } virtual CUTF8String GetErrorDesc() const override { return _Symbol.AsStringView(); } virtual bool operator==(const CExpressionBase& Other) const override { return CExprIdentifierBase::operator==(Other) && _Symbol == static_cast(Other)._Symbol; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Symbol", _Symbol.AsStringView()); } }; /** * An unresolved type identifier that is produced by desugaring, and consumed by analysis. **/ class CExprIdentifierUnresolved : public CExprIdentifierBase { public: const CSymbol _Symbol; // Used for some internal compiler-generated code that is // allowed to lookup identifiers that are otherwise restricted // (private, internal, ...) bool _bAllowUnrestrictedAccess; const bool _bAllowReservedOperators; CExprIdentifierUnresolved(const CSymbol& Symbol, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr, bool bAllowReservedOperators = false) : CExprIdentifierBase( Move(Context), Move(Qualifier)) , _Symbol(Symbol) , _bAllowUnrestrictedAccess(false) , _bAllowReservedOperators(bAllowReservedOperators) {} // Use with extreme caution! Setting this allows this identifier lookup to succeed where it // would otherwise fail due to not having permission void GrantUnrestrictedAccess() { _bAllowUnrestrictedAccess = true; } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Unresolved; } virtual bool MayHaveAttributes() const override { return true; } virtual CUTF8String GetErrorDesc() const override { return _Symbol.AsString(); } virtual bool operator==(const CExpressionBase& Other) const override { return CExprIdentifierBase::operator==(Other) && _Symbol == static_cast(Other)._Symbol; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Symbol", _Symbol.AsStringView()); } }; /** * Type identifier - MyType **/ class CExprIdentifierClass : public CExprIdentifierBase { public: UE_API CExprIdentifierClass(const CTypeType* Type, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr); UE_API const CTypeType* GetTypeType(const CSemanticProgram& Program) const; UE_API const CClass* GetClass(const CSemanticProgram& Program) const; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Class; } UE_API virtual CUTF8String GetErrorDesc() const override; }; /** * Module identifier **/ class CExprIdentifierModule : public CExprIdentifierBase { public: UE_API CExprIdentifierModule(const CModule* Module, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr); UE_API CModule const* GetModule(const CSemanticProgram& Program) const; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Module; } virtual CUTF8String GetErrorDesc() const override { return "module identifier"; } }; /** * Module alias identifier **/ class CExprIdentifierModuleAlias : public CExprIdentifierBase { public: const CModuleAlias& _ModuleAlias; CExprIdentifierModuleAlias(const CModuleAlias& ModuleAlias, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr) : CExprIdentifierBase(Move(Context), Move(Qualifier)) , _ModuleAlias(ModuleAlias) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_ModuleAlias; } virtual CUTF8String GetErrorDesc() const override { return "module alias identifier"; } }; /** * Enum identifier * @jira SOL-1013 : Shouldn't this inherit from CExprIdentifierClass? **/ class CExprEnumerationType : public CExprIdentifierBase { public: CExprEnumerationType(const CTypeType* TypeType, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr) : CExprIdentifierBase(Move(Context), Move(Qualifier)) { SetResultType(TypeType); } UE_API const CTypeType* GetTypeType(const CSemanticProgram& Program) const; UE_API const CEnumeration* GetEnumeration(const CSemanticProgram& Program) const; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Enum; } virtual CUTF8String GetErrorDesc() const override { return "enum type identifier"; } }; /** * Interface identifier * @jira SOL-1013 : Shouldn't this inherit from CExprIdentifierClass? **/ class CExprInterfaceType : public CExprIdentifierBase { public: // Identified type CExprInterfaceType(const CTypeType* TypeType, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr) : CExprIdentifierBase(Move(Context), Move(Qualifier)) { SetResultType(TypeType); } const CTypeType* GetTypeType(const CSemanticProgram& Program) const { return &GetResultType(Program)->GetNormalType().AsChecked(); } UE_API const CInterface* GetInterface(const CSemanticProgram& Program) const; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Interface; } virtual CUTF8String GetErrorDesc() const override { return "interface type identifier"; } }; /** * Local or class identifier - temp, arg, captured **/ class CExprIdentifierData : public CExprIdentifierBase { public: // The variable this expression is referring to const CDataDefinition& _DataDefinition; UE_API CExprIdentifierData(const CSemanticProgram& Program, const CDataDefinition& DataDefinition, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr); virtual const CSymbol& GetName() const { return _DataDefinition.GetName(); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Data; } virtual CUTF8String GetErrorDesc() const override { return _DataDefinition.AsNameStringView(); } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual bool operator==(const CExpressionBase& Other) const override { return CExprIdentifierBase::operator==(Other) && &_DataDefinition == &static_cast(Other)._DataDefinition; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("DataDefinition", _DataDefinition); } }; /** * Access to a type alias */ class CExprIdentifierTypeAlias : public CExprIdentifierBase { public: // The type alias this expression is referring to const CTypeAlias& _TypeAlias; UE_API CExprIdentifierTypeAlias(const CTypeAlias& TypeAlias, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_TypeAlias; } virtual CUTF8String GetErrorDesc() const override { return "type alias identifier"; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual bool operator==(const CExpressionBase& Other) const override { return CExprIdentifierBase::operator==(Other) && &_TypeAlias == &static_cast(Other)._TypeAlias; } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; }; /** * Access to a type variable */ class CExprIdentifierTypeVariable : public CExprIdentifierBase { public: // The type variable this expression is referring to const CTypeVariable& _TypeVariable; UE_API CExprIdentifierTypeVariable(const CTypeVariable& TypeVariable, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_TypeVariable; } virtual CUTF8String GetErrorDesc() const override { return "type variable identifier"; } virtual bool operator==(const CExpressionBase& Other) const override { return CExprIdentifierBase::operator==(Other) && &_TypeVariable == &static_cast(Other)._TypeVariable; } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; }; /** * Access to instance function members **/ class CExprIdentifierFunction : public CExprIdentifierBase { public: const CFunction& _Function; // `CFlowType`s created as part of instantiating `_Function`. TArray _InstantiatedTypeVariables; const CTypeBase* _ConstructorNegativeReturnType; const bool _bSuperQualified; CExprIdentifierFunction( const CFunction& Function, const CTypeBase* ResultType, TSPtr&& Context = nullptr, TSPtr&& Qualifier = nullptr) : CExprIdentifierFunction(Function, {}, ResultType, nullptr, Move(Context), Move(Qualifier), false) {} UE_API CExprIdentifierFunction( const CFunction& Function, TArray InstTypeVariables, const CTypeBase* ResultType, const CTypeBase* ConstructorNegativeReturnType, TSPtr&& Context, TSPtr&& Qualifier, bool bSuperQualified); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_Function; } virtual CUTF8String GetErrorDesc() const override { return "function identifier"; } virtual bool operator==(const CExpressionBase& Other) const override { return CExprIdentifierBase::operator==(Other) && &_Function == &static_cast(Other)._Function; } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; }; /** * An overloaded function identifier that hasn't been resolved to a specific overload. */ class CExprIdentifierOverloadedFunction : public CExprIdentifierBase { public: const TArray _FunctionOverloads; bool _bConstructor; const CSymbol _Symbol; const CTypeBase* _TypeOverload; // Used for some internal compiler-generated code that is // allowed to lookup identifiers that are otherwise restricted // (private, internal, ...) bool _bAllowUnrestrictedAccess; UE_API CExprIdentifierOverloadedFunction( TArray&& OverloadedFunctions, bool bConstructor, const CSymbol Symbol, const CTypeBase* OverloadedType, TSPtr&& Context, TSPtr&& Qualifier, const CTypeBase* Type); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Identifier_OverloadedFunction; } virtual CUTF8String GetErrorDesc() const override { return "overloaded function identifier"; } virtual bool operator==(const CExpressionBase& Other) const override { return CExprIdentifierBase::operator==(Other) && _FunctionOverloads == static_cast(Other)._FunctionOverloads && _TypeOverload == static_cast(Other)._TypeOverload; } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; }; /** * Represents all definitions (and assignments) supported by Verse. * They are: * element:valueDomain=value e.g. x:int=5 e.g. f(x:int):int=x*x * element:valueDomain e.g. x:int e.g. f(x:int):int * element=value e.g. x=5 e.g. f(x:int)=x*x */ class CExprDefinition : public CExpressionBase { public: CExprDefinition(TSPtr&& Element, TSPtr&& ValueDomain, TSPtr&& Value, EVstMappingType VstMappingType = EVstMappingType::Ast) : CExpressionBase(VstMappingType) { if (Element) { SetElement(Move(Element.AsRef())); } if (ValueDomain) { SetValueDomain(Move(ValueDomain.AsRef())); } if (Value) { SetValue(Move(Value.AsRef())); } } explicit CExprDefinition(EVstMappingType VstMappingType = EVstMappingType::Ast) : CExpressionBase(VstMappingType) {} const TSPtr& Element() const { return _Element; } void SetElement(TSRef&& Element) { _Element = Move(Element); } TSPtr TakeElement() { return Move(_Element); } void SetName(CSymbol Name) { _Name = Name; } CSymbol GetName() const { return _Name; } bool IsNamed() const { return !_Name.IsNull(); } const TSPtr& ValueDomain() const { return _ValueDomain; } void SetValueDomain(TSRef&& ValueDomain) { _ValueDomain = Move(ValueDomain); } TSPtr TakeValueDomain() { return Move(_ValueDomain); } const TSPtr& Value() const { return _Value; } void SetValue(TSRef&& Value) { _Value = Move(Value); } TSPtr TakeValue() { return Move(_Value); } virtual bool CanBePathSegment(const TMacroSymbols& MacroSymbols) const override { return Value() && Value()->CanBePathSegment(MacroSymbols); } // CAstNode interface. virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition; } virtual CUTF8String GetErrorDesc() const override { return CUTF8String("definition"); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Element", _Element); Visitor.Visit("ValueDomain", _ValueDomain); Visitor.Visit("Value", _Value); } virtual bool MayHaveAttributes() const override { return true; } // CExpressionBase interface. UE_API virtual bool operator==(const CExpressionBase& Other) const override; UE_API virtual bool CanFail(const CAstPackage* Package) const override; UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; protected: TSPtr _Element; TSPtr _ValueDomain; TSPtr _Value; CSymbol _Name; // If non-null, then usage requires being ?named and presence of `_Value` indicates that it has a default }; enum class EMacroClauseTag : uint32_t { None = 0x1 << 0, Of = 0x1 << 1, Do = 0x1 << 2 }; constexpr EMacroClauseTag operator| (EMacroClauseTag A, EMacroClauseTag B) { return static_cast(static_cast(A) | static_cast(B)); } constexpr bool HasAnyTags(EMacroClauseTag A, EMacroClauseTag B) { return 0 != (static_cast(A) & static_cast(B)); } constexpr bool HasAllTags(EMacroClauseTag A, EMacroClauseTag RequiredTags) { return RequiredTags == static_cast(static_cast(A) & static_cast(RequiredTags)); } inline const char* MacroClauseTagAsCString(EMacroClauseTag Tag) { switch(Tag) { case EMacroClauseTag::None: return "None"; case EMacroClauseTag::Of: return "Of"; case EMacroClauseTag::Do: return "Do"; default: ULANG_UNREACHABLE(); }; } inline const char* MacroClauseFormAsCString(Verse::Vst::Clause::EForm Form) { switch(Form) { case Verse::Vst::Clause::EForm::Synthetic: return "Synthetic"; case Verse::Vst::Clause::EForm::NoSemicolonOrNewline: return "NoSemicolonOrNewline"; case Verse::Vst::Clause::EForm::HasSemicolonOrNewline: return "HasSemicolonOrNewline"; case Verse::Vst::Clause::EForm::IsAppendAttributeHolder: return "IsAppendAttributeHolder"; case Verse::Vst::Clause::EForm::IsPrependAttributeHolder: return "IsPrependAttributeHolder"; default: ULANG_UNREACHABLE(); }; } /** A macro call of the form m1{}, m2(){}, or more generally m(){}keyword_1{}keyword_2{}...keyword_N{} */ class CExprMacroCall : public CExpressionBase { public: /** A macro is an identifier followed by any number of tagged clauses. This class represents a single clause. */ class CClause { public: using EForm = Verse::Vst::Clause::EForm; CClause(EMacroClauseTag Tag, EForm Form, TArray>&& Exprs) : _Tag(Tag) , _Form(Form) , _Exprs(Move(Exprs)) {} EMacroClauseTag Tag() const { return _Tag; } EForm Form() const { return _Form; } TArray> const & Exprs() const { return _Exprs; } TArray> & Exprs() { return _Exprs; } private: EMacroClauseTag _Tag; Verse::Vst::Clause::EForm _Form; TArray> _Exprs; }; public: CExprMacroCall( TSRef&& Name, int32_t NumClauses = 0 ) : _Name(Move(Name)) , _Clauses() { if (NumClauses != 0) { _Clauses.Reserve(NumClauses); } } void AppendClause(CClause&& Clause) { _Clauses.Add(Move(Clause)); } TSRef const & Name() const { return _Name; } TSRef & Name() { return _Name; } void SetName(TSRef&& NewName) { _Name = Move(NewName); } TSRef&& TakeName() { return Move(_Name); } TArray const& Clauses() const { return _Clauses; } TArray & Clauses() { return _Clauses; } TArray&& TakeClauses() { return Move(_Clauses); } virtual bool CanBePathSegment(const TMacroSymbols& MacroSymbols) const override { CSymbol Symbol; if (_Name->GetNodeType() == EAstNodeType::Identifier_BuiltInMacro) { Symbol = static_cast(_Name.Get())->_Symbol; } else if (_Name->GetNodeType() == EAstNodeType::Identifier_Unresolved) { Symbol = static_cast(_Name.Get())->_Symbol; } if (!Symbol.IsNull()) { for (CSymbol MacroSymbol : MacroSymbols) { if (MacroSymbol == Symbol) { return true; } } } return false; } private: // Inherited via CExpressionBase virtual EAstNodeType GetNodeType() const override { return EAstNodeType::MacroCall; } virtual CUTF8String GetErrorDesc() const override { return "macro invocation"; } virtual bool operator==(const CExpressionBase& Other) const override { return BaseCompare(*this,Other); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Name", _Name); Visitor.BeginArray("Clauses", _Clauses.Num()); for (const CClause& Clause : _Clauses) { Visitor.VisitArray("Exprs", Clause.Exprs()); } Visitor.EndArray(); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.BeginArray("Clauses", _Clauses.Num()); for (const CClause& Clause : _Clauses) { Visitor.VisitImmediate("Tag", MacroClauseTagAsCString(Clause.Tag())); Visitor.VisitImmediate("Form", MacroClauseFormAsCString(Clause.Form())); } Visitor.EndArray(); } private: TSRef _Name; TArray _Clauses; }; /** * Routine call - expr1.call(expr2, expr3) **/ class CExprInvocation : public CExpressionBase { public: enum class EBracketingStyle { Undefined, Parentheses, SquareBrackets, }; EBracketingStyle _CallsiteBracketStyle = EBracketingStyle::Undefined; template CExprInvocation(EBracketingStyle CallsiteBracketStyle, TCallee&& Callee, TArgument&& Argument) : _CallsiteBracketStyle(CallsiteBracketStyle) , _Callee(ForwardArg(Callee)) , _Argument(ForwardArg(Argument)) {} template CExprInvocation(EBracketingStyle CallsiteBracketStyle, TCallee&& Callee, TArgument&& Argument, const CFunctionType* ResolvedCalleeType, const CTypeBase* ResultType) : CExprInvocation(CallsiteBracketStyle, ForwardArg(Callee), ForwardArg(Argument)) { SetResolvedCalleeType(ResolvedCalleeType); SetResultType(ResultType); } explicit CExprInvocation(TSRef&& Argument) : _Argument(Move(Argument)) {} CExprInvocation(CExprInvocation&& Rhs) : _Callee(Move(Rhs._Callee)) , _Argument(Move(Rhs._Argument)) , _ResolvedCalleeType(Rhs._ResolvedCalleeType) {} const TSPtr& GetCallee() const { return _Callee; } TSPtr&& TakeCallee() { return Move(_Callee); } void SetCallee(TSPtr&& Callee) { _Callee = Move(Callee); } const TSPtr& GetArgument() const { return _Argument; } TSPtr&& TakeArgument() { return Move(_Argument); } void SetArgument(TSPtr&& Arguments) { _Argument = Move(Arguments); } UE_API const CFunctionType* GetResolvedCalleeType() const; void SetResolvedCalleeType(const CFunctionType* ResolvedCalleeType) { _ResolvedCalleeType = ResolvedCalleeType; } /// Get the function that this invocation calls UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; UE_API virtual bool CanFail(const CAstPackage* Package) const override; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_Invocation; } virtual CUTF8String GetErrorDesc() const override { return "invocation"; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Callee", _Callee); Visitor.Visit("Argument", _Argument); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); const char* BracketStyleString; switch(_CallsiteBracketStyle) { case EBracketingStyle::Undefined: BracketStyleString = "Undefined"; break; case EBracketingStyle::Parentheses: BracketStyleString = "Parentheses"; break; case EBracketingStyle::SquareBrackets: BracketStyleString = "SquareBrackets"; break; default: ULANG_UNREACHABLE(); }; Visitor.VisitImmediate("CallsiteBracketStyle", BracketStyleString); if (_ResolvedCalleeType) { Visitor.VisitImmediate("ResolvedCalleeType", _ResolvedCalleeType); } } UE_API virtual bool operator==(const CExpressionBase& Other) const override; private: // Callee subexpression - The function to call. TSPtr _Callee; // Argument - possibly a tuple of expressions, included named expressions TSPtr _Argument; // More often than not, the function type could be inferred from _Callee, but in the case of generics, // you want to store the function type of the resolved generic. const CFunctionType* _ResolvedCalleeType{nullptr}; }; VERSECOMPILER_API const CExprIdentifierFunction* GetConstructorInvocationCallee(const CExprInvocation&); VERSECOMPILER_API const CExprIdentifierFunction* GetConstructorInvocationCallee(const CExpressionBase&); VERSECOMPILER_API bool IsConstructorInvocation(const CExprInvocation&); VERSECOMPILER_API bool IsConstructorInvocation(const CExpressionBase&); /** * Tuple element access `TupleExpr(Idx)` **/ class CExprTupleElement : public CExpressionBase { public: // Expression that results in tuple to access element from TSPtr _TupleExpr; // Index resolved from `_ElemIdxExpr` Integer _ElemIdx; // Expression used to determine index - currently must be an integer literal CExprNumber. // `_ElemIdx` is resolved form. This is stored just to track source information in VST nodes. TSPtr _ElemIdxExpr; CExprTupleElement(CExprInvocation& Invocation) : _TupleExpr(Invocation.TakeCallee()), _ElemIdx(-1) { Invocation.GetMappedVstNode()->AddMapping(this); } CExprTupleElement(TSPtr TupleExpr, Integer ElemIdx, const Verse::Vst::Node* MappedVstNode) : _TupleExpr(TupleExpr), _ElemIdx(ElemIdx) { _MappedVstNode = MappedVstNode; } UE_API virtual bool CanFail(const CAstPackage* Package) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("TupleExpr", _TupleExpr); Visitor.Visit("ElemIdxExpr", _ElemIdxExpr); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("ElemIdx", _ElemIdx); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_TupleElement; } virtual CUTF8String GetErrorDesc() const override { return "tuple element access"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; }; /** * Assignment -- expr1 = expr2, expr1 := expr2, expr1 += expr2, etc. */ class CExprAssignment : public CExpressionBase { public: using EOp = Verse::Vst::Assignment::EOp; CExprAssignment(EOp Op, TSPtr&& Lhs, TSPtr&& Rhs) : _Op(Op), _Lhs(Move(Lhs)), _Rhs(Move(Rhs)) {} EOp Op() const { return _Op; } const TSPtr& Lhs() const { return _Lhs; } const TSPtr& Rhs() const { return _Rhs; } TSPtr&& TakeLhs() { return Move(_Lhs); } TSPtr&& TakeRhs() { return Move(_Rhs); } void SetLhs(TSPtr&& Lhs) { _Lhs = Move(Lhs); } void SetRhs(TSPtr&& Rhs) { _Rhs = Move(Rhs); } //~ Begin CExpressionBase interface virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { const CExpressionBase* AsyncExpr = _Lhs->FindFirstAsyncSubExpr(Program); return AsyncExpr ? AsyncExpr : _Rhs->FindFirstAsyncSubExpr(Program); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Assignment; } virtual CUTF8String GetErrorDesc() const override { return "assignment"; } virtual bool CanFail(const CAstPackage* Package) const override { return _Lhs->CanFail(Package) || _Rhs->CanFail(Package); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Lhs", _Lhs); Visitor.Visit("Rhs", _Rhs); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Op", Verse::Vst::AssignmentOpAsCString(_Op)); } UE_API virtual bool operator==(const CExpressionBase& Other) const override; //~ End CExpressionBase interface private: const EOp _Op; TSPtr _Lhs; TSPtr _Rhs; }; struct SAssignmentLhsIdentifier { TSPtr PointerToReference; TSPtr IdentifierData; }; VERSECOMPILER_API TOptional IdentifierOfAssignmentLhs(const CExprAssignment* Assignment /* can be null */); VERSECOMPILER_API bool HasImplicitClassSelf(const CExprIdentifierData* Expr /* can be null */); VERSECOMPILER_API bool IsClassMemberAccess(const CExprIdentifierData* Expr /* can be null */); /** * Base class of unary operators. */ class CExprUnaryOp : public CExpressionBase { public: explicit CExprUnaryOp(TSPtr&& Operand, EVstMappingType VstMappingType = EVstMappingType::Ast) : CExpressionBase(VstMappingType) { SetOperand(Move(Operand)); } const TSPtr& Operand() const { return _Operand; } TSPtr&& TakeOperand() { return Move(_Operand); } void SetOperand(TSPtr&& Operand) { _Operand = Move(Operand); } virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return _Operand.IsValid() ? _Operand->FindFirstAsyncSubExpr(Program) : nullptr; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Operand", _Operand); } private: TSPtr _Operand; }; class CExprUnaryArithmetic : public CExprInvocation { public: enum class EOp { Negate }; CExprUnaryArithmetic(EOp Op, TSRef&& Rhs) : CExprInvocation(Move(Rhs)) , _Op(Op) { } TSRef Operand() const { return GetArgument().AsRef(); } void SetOperand(TSPtr&& NewOperand) { SetArgument(Move(NewOperand)); } TSPtr&& TakeOperand() { return TakeArgument(); } EOp Op() const { return _Op; } private: virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_UnaryArithmetic; } virtual bool CanFail(const CAstPackage* Package) const override { return false; } virtual CUTF8String GetErrorDesc() const override { return "unary negation"; } virtual bool operator==(const CExpressionBase& Other) const override { return CExprInvocation::operator==(Other); } EOp _Op; }; class CExprBinaryArithmetic : public CExprInvocation { public: enum class EOp { Add, Sub, Mul, Div }; CExprBinaryArithmetic(EOp Op, TSRef&& Argument) : CExprInvocation(Move(Argument)) , _Op(Op) { } EOp Op() const { return _Op; } private: virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_BinaryArithmetic; } virtual CUTF8String GetErrorDesc() const override { switch (_Op) { case EOp::Add: return "binary addition"; case EOp::Sub: return "binary subtraction"; case EOp::Mul: return "binary multiplication"; case EOp::Div: return "binary division"; default: ULANG_UNREACHABLE(); }; } virtual bool operator==(const CExpressionBase& Other) const override { return CExprInvocation::operator==(Other) && _Op == static_cast(Other)._Op; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); const char* OpString; switch(_Op) { case EOp::Add: OpString = "Add"; break; case EOp::Sub: OpString = "Sub"; break; case EOp::Mul: OpString = "Mul"; break; case EOp::Div: OpString = "Div"; break; default: ULANG_UNREACHABLE(); }; Visitor.VisitImmediate("Op", OpString); } EOp _Op; }; /** * Short-circuit evaluation of a Boolean and **/ class CExprShortCircuitAnd : public CExprBinaryOp { public: CExprShortCircuitAnd(TSPtr&& Lhs, TSPtr&& Rhs) : CExprBinaryOp(Move(Lhs), Move(Rhs)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_ShortCircuitAnd; } virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override { return Rhs()->GetResultType(Program); } virtual bool CanFail(const CAstPackage* Package) const override { return true; } virtual CUTF8String GetErrorDesc() const override { return "logical '&&'"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; }; /** * Short-circuit evaluation of a Boolean or **/ class CExprShortCircuitOr : public CExprBinaryOp { public: CExprShortCircuitOr(TSPtr&& Lhs, TSPtr&& Rhs, const CTypeBase* JoinType = nullptr) : CExprBinaryOp(Move(Lhs), Move(Rhs)) { if (JoinType != nullptr) { SetResultType(JoinType); } } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_ShortCircuitOr; } virtual bool CanFail(const CAstPackage* Package) const override { return Rhs()->CanFail(Package); } virtual CUTF8String GetErrorDesc() const override { return "logical '||'"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; private: }; /** * Logical not operator */ class CExprLogicalNot : public CExprUnaryOp { public: CExprLogicalNot(TSPtr&& Operand): CExprUnaryOp(Move(Operand)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_LogicalNot; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual bool CanFail(const CAstPackage* Package) const override { return true; } virtual CUTF8String GetErrorDesc() const override { return "logical 'not'"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; }; /** * Comparison operators. */ class CExprComparison : public CExprInvocation { public: using EOp = Verse::Vst::BinaryOpCompare::op; CExprComparison(EOp Op, TSRef&& Argument) : CExprInvocation(Move(Argument)) , _Op(Op) { _CallsiteBracketStyle = CExprInvocation::EBracketingStyle::SquareBrackets; } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_Comparison; } UE_API virtual CUTF8String GetErrorDesc() const override; UE_API virtual bool operator==(const CExpressionBase& Other) const override; virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Op", Verse::Vst::BinaryCompareOpAsCString(_Op)); } EOp Op() const { return _Op; } private: EOp _Op; }; /** * Query the value of a boolean or option value. **/ class CExprQueryValue : public CExprInvocation { public: CExprQueryValue(TSRef&& Operand) : CExprInvocation(Move(Operand)) { _CallsiteBracketStyle = CExprInvocation::EBracketingStyle::SquareBrackets; } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_QueryValue; } virtual CUTF8String GetErrorDesc() const override { return "postfix '?' operator"; } }; /** * Box an option value */ class CExprMakeOption : public CExprUnaryOp { public: CExprMakeOption(const CTypeBase* Type, TSPtr&& Operand) : CExprUnaryOp(Move(Operand)) { SetResultType(Type); } COptionType const* GetOptionType(const CSemanticProgram& Program) const { const CTypeBase* MyResultType = GetResultType(Program); return &(MyResultType->GetNormalType().AsChecked()); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_MakeOption; } virtual bool CanFail(const CAstPackage* Package) const override { return false; } virtual CUTF8String GetErrorDesc() const override { return "option value constructor"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; }; /** * Create an array value * Can have zero or more subexpressions */ class CExprMakeArray : public CExprCompoundBase { public: CExprMakeArray(int32_t ReserveSubExprNum) : CExprCompoundBase(ReserveSubExprNum) {} const CArrayType* GetArrayType(const CSemanticProgram& Program) const { return &GetResultType(Program)->GetNormalType().AsChecked(); } //~ Begin CExpressionBase interface virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_MakeArray; } virtual CUTF8String GetErrorDesc() const override { return "array value"; } //~ End CExpressionBase interface }; /** * Create a map value * Can have zero or more subexpressions */ class CExprMakeMap : public CExprCompoundBase { public: explicit CExprMakeMap(int32_t ReserveSubExprNum = 0) : CExprCompoundBase(ReserveSubExprNum) {} const CMapType* GetMapType(const CSemanticProgram& Program) const { return &GetResultType(Program)->GetNormalType().AsChecked(); } //~ Begin CExpressionBase interface virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_MakeMap; } virtual CUTF8String GetErrorDesc() const override { return "map value"; } UE_API virtual bool CanFail(const CAstPackage* Package) const override; //~ End CExpressionBase interface }; /** * Create a tuple value */ class CExprMakeTuple : public CExprCompoundBase { public: using CExprCompoundBase::CExprCompoundBase; const CTupleType* GetTupleType(const CSemanticProgram& Program) const { return &GetResultType(Program)->GetNormalType().AsChecked(); } //~ Begin CExpressionBase interface virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_MakeTuple; } virtual CUTF8String GetErrorDesc() const override { return "tuple value"; } //~ End CExpressionBase interface }; /** * Create a range value */ class CExprMakeRange : public CExpressionBase { public: TSRef _Lhs; TSRef _Rhs; CExprMakeRange(TSRef&& Lhs, TSRef&& Rhs) : _Lhs(Move(Lhs)) , _Rhs(Move(Rhs)) {} void SetLhs(TSRef&& Lhs) { _Lhs = Move(Lhs); } void SetRhs(TSRef&& Rhs) { _Rhs = Move(Rhs); } //~ Begin CExpressionBase interface virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_MakeRange; } virtual bool CanFail(const CAstPackage* Package) const override { return _Lhs->CanFail(Package) || _Rhs->CanFail(Package); } virtual CUTF8String GetErrorDesc() const override { return "range constructor"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; //~ End CExpressionBase interface virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Lhs", _Lhs); Visitor.Visit("Rhs", _Rhs); } }; /** * Invoke a type as a function on a value - type(expr) or type[expr]. */ class CExprInvokeType : public CExpressionBase { public: const CTypeBase* _NegativeType; const bool _bIsFallible; const TSPtr _TypeAst; const TSRef _Argument; UE_API CExprInvokeType(const CTypeBase* NegativeType, const CTypeBase* PositiveType, bool bIsFallible, TSPtr&& TypeAst, TSRef&& Argument); //~ Begin CExpressionBase interface UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; virtual bool CanFail(const CAstPackage* Package) const override { return _bIsFallible || _Argument->CanFail(Package); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_Type; } virtual CUTF8String GetErrorDesc() const override { return "type invocation"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("TypeAst", _TypeAst); Visitor.Visit("Argument", _Argument); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("NegativeType", _NegativeType); Visitor.VisitImmediate("bIsFallible", _bIsFallible); } //~ End CExpressionBase interface }; /** * Read the value of a variable. */ class CExprPointerToReference : public CExprUnaryOp { public: CExprPointerToReference(TSRef&& Variable) : CExprUnaryOp(Move(Variable)) {} //~ Begin CExpressionBase interface virtual bool CanFail(const CAstPackage* Package) const override { return Operand()->CanFail(Package); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_PointerToReference; } virtual CUTF8String GetErrorDesc() const override { return "pointer to reference"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; //~ End CExpressionBase interface }; /** * Evaluate operand to an l-expression. */ class CExprSet : public CExprUnaryOp { using CExprUnaryOp::CExprUnaryOp; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_Set; } virtual bool CanFail(const CAstPackage* Package) const override { return Operand()->CanFail(Package); } virtual CUTF8String GetErrorDesc() const override { return "set"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; }; /** * Create a new pointer from an initial value. */ class CExprNewPointer : public CExpressionBase { public: const TSRef _Value; CExprNewPointer(const CPointerType* PointerType, TSRef&& Value) : CExpressionBase(PointerType) , _Value(Move(Value)) {} //~ Begin CExpressionBase interface virtual bool CanFail(const CAstPackage* Package) const override { return _Value->CanFail(Package); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_NewPointer; } virtual CUTF8String GetErrorDesc() const override { return "pointer new"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Value", _Value); } //~ End CExpressionBase interface }; /** * Evaluates the value of an expression yielding a reference type. */ class CExprReferenceToValue : public CExprUnaryOp { public: UE_API CExprReferenceToValue(TSPtr&& Operand); //~ Begin CExpressionBase interface virtual bool CanFail(const CAstPackage* Package) const override { return Operand()->CanFail(Package); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_ReferenceToValue; } virtual CUTF8String GetErrorDesc() const override { return "convert reference to value"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Invoke_ReferenceToValue && (*this) == static_cast(Other); } //~ End CExpressionBase interface }; /** * Code block - `{expr1; expr2}` or `do {expr1; expr2}` * Can have zero or more subexpressions **/ class CExprCodeBlock : public CExprCompoundBase { public: // The scope containing locals for this block TSPtr _AssociatedScope; // Methods explicit CExprCodeBlock(int32_t ReserveSubExprNum = 0) : CExprCompoundBase(ReserveSubExprNum) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_CodeBlock; } virtual CUTF8String GetErrorDesc() const override { return "code block"; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; }; class CExprLet : public CExprCompoundBase { using CExprCompoundBase::CExprCompoundBase; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_Let; } virtual CUTF8String GetErrorDesc() const override { return "let"; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; }; /** * Return statement - return expr **/ class CExprReturn : public CExpressionBase { public: CExprReturn() {} CExprReturn(TSPtr&& Result, const CFunction* Function = nullptr) { SetResult(Move(Result)); SetFunction(Function); } const TSPtr& Result() const { return _Result; } void SetResult(TSPtr&& Result) { _Result = Move(Result); } const CFunction* Function() const { return _Function; } void SetFunction(const CFunction* Function) { _Function = Function; } virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return _Result.IsValid() ? _Result->FindFirstAsyncSubExpr(Program) : nullptr; } virtual bool CanFail(const CAstPackage* Package) const override { return _Result.IsValid() && _Result->CanFail(Package); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_Return; } virtual CUTF8String GetErrorDesc() const override { return "return"; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual bool MayHaveAttributes() const override { return true; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Result", _Result); } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Flow_Return && IsSubExprEqual(_Result, static_cast(Other)._Result); } private: // Result expression TSPtr _Result; // The function being returned from. const CFunction* _Function{nullptr}; }; /** * Conditional with failable tests- if (test[]) {clause1}, if (test[]) {clause1} else {else_clause} **/ class CExprIf : public CExpressionBase { public: CExprIf(TSRef&& Condition, TSPtr&& ThenClause, TSPtr&& ElseClause = nullptr) : _Condition(Move(Condition)) , _ThenClause(Move(ThenClause)) , _ElseClause(Move(ElseClause)) {} const TSRef& GetCondition() const { return _Condition; } void SetCondition(TSRef&& Condition) { _Condition = Move(Condition); } const TSPtr& GetThenClause() const { return _ThenClause; } void SetThenClause(TSPtr&& ThenClause) { _ThenClause = Move(ThenClause); } const TSPtr& GetElseClause() const { return _ElseClause; } void SetElseClause(TSPtr&& ElseClause) { _ElseClause = Move(ElseClause); } UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; UE_API virtual bool CanFail(const CAstPackage* Package) const override; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_If; } virtual CUTF8String GetErrorDesc() const override { return "if"; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Condition", _Condition); Visitor.Visit("ThenClause", _ThenClause); Visitor.Visit("ElseClause", _ElseClause); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("bIsFilter", _bIsFilter); } UE_API virtual bool operator==(const CExpressionBase& Other) const override; // If can be used as a filter in a for(..) { }. Code generation need to know. bool _bIsFilter{ false }; private: TSRef _Condition; TSPtr _ThenClause; TSPtr _ElseClause; }; /** * Bounded iteration **/ class CExprIteration : public CExpressionBase // Note that CExprSyncIterated, CExprRushIterated and CExprRaceIterated are subclasses { public: CExprIteration() {} void SetBody(TSPtr&& Body) { _Body = Move(Body); } void AddFilter(TSRef&& Filter) { _Filters.Add(Move(Filter)); } UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; virtual bool CanFail(const CAstPackage* Package) const override { return _Body->CanFail(Package); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_Iteration; } virtual CUTF8String GetErrorDesc() const override { return "for"; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.VisitArray("Filters", _Filters); Visitor.Visit("Body", _Body); } UE_API virtual bool operator==(const CExpressionBase& Other) const override; public: // The scope containing the variables used for iterating TSPtr _AssociatedScope; // The "filters" that are used TArray> _Filters; // Expression to evaluate for every iteration that gets past the filters step TSPtr _Body; }; /** * Iteration based concurrency primitives - sync(item:collection){}, rush(item:collection){} or race(item:collection){} **/ class CExprConcurrentIteratedBase : public CExprIteration { public: virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return this; } }; /** * Iterated Sync (collection form) concurrency primitive - sync(item:collection) {_coro1() _coro2()} **/ class CExprSyncIterated : public CExprConcurrentIteratedBase { public: virtual CUTF8String GetErrorDesc() const override { return "sync(:){}"; } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Concurrent_SyncIterated; } }; /** * Iterated Rush (collection form) concurrency primitive - rush(item:collection) {_coro1() _coro2()} **/ class CExprRushIterated : public CExprConcurrentIteratedBase { public: virtual CUTF8String GetErrorDesc() const override { return "rush(:){}"; } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Concurrent_RushIterated; } }; /** * Iterated Race (collection form) concurrency primitive - race(item:collection) {_coro1() _coro2()} **/ class CExprRaceIterated : public CExprConcurrentIteratedBase { public: virtual CUTF8String GetErrorDesc() const override { return "race(:){}"; } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Concurrent_RaceIterated; } }; /// Base class for all expressions that form a type out of input type(s) class CExprTypeFormer : public CExpressionBase { public: // Metatype of the actual type formed. const CTypeType* _TypeType{nullptr}; virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return nullptr; } virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override { return _TypeType; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("TypeType", _TypeType); } }; class CExprUnaryTypeFormer : public CExprTypeFormer { public: CExprUnaryTypeFormer(TSRef&& InnerTypeAst): _InnerTypeAst(Move(InnerTypeAst)) {} const TSRef& GetInnerTypeAst() const { return _InnerTypeAst; } void SetInnerTypeAst(TSRef&& InnerTypeAst) { _InnerTypeAst = Move(InnerTypeAst); } // CExpressionBase interface. virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return _InnerTypeAst->FindFirstAsyncSubExpr(Program); } // CAstNode interface. virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("InnerTypeAst", _InnerTypeAst); } private: TSRef _InnerTypeAst; }; class CExprArrayTypeFormer : public CExprUnaryTypeFormer { public: CExprArrayTypeFormer(TSRef&& InnerTypeAst): CExprUnaryTypeFormer(Move(InnerTypeAst)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_ArrayFormer; } virtual CUTF8String GetErrorDesc() const override { return "array type"; } bool operator==(const CExpressionBase& Other) const override { return false; } const CArrayType* GetArrayType() const { ULANG_ASSERTF(_TypeType, "GetArrayType called on unanalyzed expression"); return &_TypeType->PositiveType()->GetNormalType().AsChecked(); } }; class CExprGeneratorTypeFormer : public CExprUnaryTypeFormer { public: /// Construct an expression that takes the `ValueType` and forms an Generator of `ValueType`. CExprGeneratorTypeFormer(TSRef&& InnerTypeAst) : CExprUnaryTypeFormer(Move(InnerTypeAst)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_GeneratorFormer; } virtual CUTF8String GetErrorDesc() const override { return "generator(..)"; } virtual bool operator==(const CExpressionBase& Other) const override { return false; } const CGeneratorType* GetGeneratorType() const { ULANG_ASSERTF(_TypeType, "GetGeneratorType called on unanalyzed expression"); return &_TypeType->PositiveType()->GetNormalType().AsChecked(); } }; class CExprMapTypeFormer : public CExprTypeFormer { public: CExprMapTypeFormer(TArray>&& KeyTypeAsts, TSRef&& ValueTypeAst) : CExprTypeFormer() , _KeyTypeAsts(KeyTypeAsts) , _ValueTypeAst(ValueTypeAst) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_MapFormer; } virtual CUTF8String GetErrorDesc() const override { return "map type"; } bool operator==(const CExpressionBase& Other) const override { return false; } const TArray>& KeyTypeAsts() const { return _KeyTypeAsts; } void SetKeyTypeAst(TSRef&& KeyTypeAst, int32_t Index) { _KeyTypeAsts[Index] = Move(KeyTypeAst); } const TSRef& ValueTypeAst() const { return _ValueTypeAst; } void SetValueTypeAst(TSRef&& ValueTypeAst) { _ValueTypeAst = Move(ValueTypeAst); } const CMapType* GetMapType() const { ULANG_ASSERTF(_TypeType, "GetMapType called on unanalyzed expression"); return &_TypeType->PositiveType()->GetNormalType().AsChecked(); } // CExpressionBase interface. UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; // CAstNode interface. virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.VisitArray("KeyTypeAsts", _KeyTypeAsts); Visitor.Visit("ValueTypeAst", _ValueTypeAst); } private: TArray> _KeyTypeAsts; TSRef _ValueTypeAst; }; class CExprOptionTypeFormer : public CExprUnaryTypeFormer { public: CExprOptionTypeFormer(TSRef&& InnerTypeAst): CExprUnaryTypeFormer(Move(InnerTypeAst)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_OptionFormer; } virtual CUTF8String GetErrorDesc() const override { return "option type"; } virtual bool operator==(const CExpressionBase& Other) const override { return false; } const COptionType* GetOptionType() const { ULANG_ASSERTF(_TypeType, "GetOptionType called on unanalyzed expression"); return &_TypeType->NegativeType()->GetNormalType().AsChecked(); } }; class CExprSubtype : public CExprUnaryTypeFormer { public: /// Construct an expression that takes the `ValueType` and forms an option of `ValueType`. CExprSubtype(TSRef&& InnerTypeAst): CExprUnaryTypeFormer(Move(InnerTypeAst)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_Subtype; } virtual CUTF8String GetErrorDesc() const override { return _bCastableSubtype ? "castable_subtype(..)" : "subtype(..)"; } virtual bool operator==(const CExpressionBase& Other) const override { return false; } UE_API const CTypeType& GetSubtypeType() const; bool _bCastableSubtype = false; }; // Get or create a tuple based on `tuple(type1, type2, ...)` class CExprTupleType : public CExprTypeFormer { public: CExprTupleType(int32_t ReserveTypeExprNum = 0) { _ElementTypeExprs.Reserve(ReserveTypeExprNum); } const TSPtrArray& GetElementTypeExprs() const { return _ElementTypeExprs; } TSPtrArray& GetElementTypeExprs() { return _ElementTypeExprs; } void ReplaceElementTypeExpr(TSPtr&& TypeExpr, int32_t Index) { ULANG_ASSERTF(Index >= 0 && Index < _ElementTypeExprs.Num(), "Replacing invalid subexpression index"); _ElementTypeExprs.ReplaceAt(Move(TypeExpr), Index); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_TupleType; } virtual CUTF8String GetErrorDesc() const override { return "tuple(..)"; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.VisitArray("ElemTypeExprs", _ElementTypeExprs); } UE_API virtual bool operator==(const CExpressionBase& Other) const override; const CTupleType* GetTupleType() const { ULANG_ASSERTF(_TypeType, "GetTupleType called on unanalyzed expression"); return &_TypeType->PositiveType()->GetNormalType().AsChecked(); } protected: TSPtrArray _ElementTypeExprs; }; /** * Create a function type from a parameter and return type. * Also used on the LHS of a definition as a pattern for iteration pairs. */ class CExprArrow : public CExprTypeFormer { public: CExprArrow(TSRef&& Domain, TSRef&& Range) : _Domain(Move(Domain)) , _Range(Move(Range)) {} const TSRef& Domain() const { return _Domain; } const TSRef& Range() const { return _Range; } void SetDomain(TSRef&& NewDomain) { _Domain = Move(NewDomain); } void SetRange(TSRef&& NewRange) { _Range = Move(NewRange); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_Arrow; } virtual CUTF8String GetErrorDesc() const override { return "function type"; } virtual bool operator==(const CExpressionBase& Other) const override { return GetNodeType() == Other.GetNodeType() && IsSubExprEqual(_Domain, static_cast(Other)._Domain) && IsSubExprEqual(_Range, static_cast(Other)._Range); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Domain", _Domain); Visitor.Visit("Range", _Range); } const CFunctionType* GetFunctionType() const { ULANG_ASSERTF(_TypeType, "GetFunctionType called on unanalyzed expression"); return &_TypeType->PositiveType()->GetNormalType().AsChecked(); } private: TSRef _Domain; TSRef _Range; }; /** * Represents an initializer-list style construction for certain object types; * i.e.: Type{expr1, id=expr2, ...} */ class CExprArchetypeInstantiation : public CExpressionBase { public: UE_API CExprArchetypeInstantiation(TSRef&& ClassAst, CExprMacroCall::CClause&& BodyAst, const CTypeBase* ResultType); UE_API const CClass* GetClass(const CSemanticProgram& Program) const; const TSRefArray& Arguments() const { return _Arguments; } int32_t AppendArgument(TSRef&& Argument) { return _Arguments.Add(Move(Argument)); } virtual bool CanFail(const CAstPackage* Package) const override { for (const auto& Argument : _Arguments) { if (Argument->CanFail(Package)) { return true; } } return false; } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_ArchetypeInstantiation; } virtual CUTF8String GetErrorDesc() const override { return "archetype constructor"; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("ClassAst", _ClassAst); Visitor.VisitArray("BodyAstExprs", _BodyAst.Exprs()); Visitor.BeginArray("Arguments", _Arguments.Num()); for(const TSRef& Argument : _Arguments) { Visitor.VisitElement(Argument); } Visitor.EndArray(); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("BodyAstTag", MacroClauseTagAsCString(_BodyAst.Tag())); Visitor.VisitImmediate("BodyAstForm", MacroClauseFormAsCString(_BodyAst.Form())); } UE_API bool operator==(const CExpressionBase& Other) const override; TSRef _ClassAst; CExprMacroCall::CClause _BodyAst; private: TSRefArray _Arguments; }; /** * Block based concurrency primitives - sync{}, rush{} or race{} * Can have two or more async subexpressions - 0 or 1 is a warning or error. **/ class CExprConcurrentBlockBase : public CExprCompoundBase { public: explicit CExprConcurrentBlockBase(int32_t ReserveSubExprNum = 0) : CExprCompoundBase(ReserveSubExprNum) {} virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return this; } }; /** * Sync concurrency primitive - sync {_coro1() _coro2()} * 2+ Async expressions run concurrently and next expression executed when *all* expressions * completed. Its overall result is the result of the last expression in the block to complete * (using most specific common compatible result type of all expressions or no result if there is * no common type). Expressions may only be async (such as coroutines). **/ class CExprSync : public CExprConcurrentBlockBase { public: virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Concurrent_Sync; } virtual CUTF8String GetErrorDesc() const override { return "sync{}"; } }; /** * Race concurrency primitive - race {_coro1() _coro2()} * 2+ Async expressions run concurrently and next expression executed when *fastest/first* * expression completed and all other expressions are aborted. Its overall result is result of * first expression in block to complete (using most specific common compatible result type of all * expressions or no result if there is no common type). **/ class CExprRace : public CExprConcurrentBlockBase { public: virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Concurrent_Race; } virtual CUTF8String GetErrorDesc() const override { return "race{}"; } }; /** * Rush concurrency primitive - rush {_coro1() _coro2()} * 2+ Async expressions run concurrently and next expression executed when *fastest/first* * expression completed and all other expressions continue independently until each is fully * completed. Its overall result is the result of the first expression in the block to complete * (using most specific common compatible result type of all expressions or no result if there is * no common type). **/ class CExprRush : public CExprConcurrentBlockBase { public: virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Concurrent_Rush; } virtual CUTF8String GetErrorDesc() const override { return "rush{}"; } }; /** * Expressions with a sub-block - branch, spawn, loop, defer **/ class CExprSubBlockBase : public CExpressionBase { protected: // Sub-expression from block - either single expression or block of expressions TSPtr _BlockExpr; public: void SetExpr(TSPtr&& Expr) { _BlockExpr = Move(Expr); } const TSPtr& Expr() const { return _BlockExpr; } virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return nullptr; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("BlockExpr", _BlockExpr); } virtual bool operator==(const CExpressionBase& Other) const override { return (Other.GetNodeType() == GetNodeType()) && IsSubExprEqual(_BlockExpr, static_cast(Other)._BlockExpr); } }; /** * Branch concurrency primitive - branch {Coro()} * One async expression started and next expression executed immediately while the started * expression continues independently until completion or the surrounding context (such as a * coroutine) completes in which case it aborts early. Result is a handle to the invoked coroutine / invoked * expression (also known as a future or promise) tracking the expression. **/ class CExprBranch : public CExprSubBlockBase { public: virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Concurrent_Branch; } virtual CUTF8String GetErrorDesc() const override { return "branch"; } }; /** * Spawn concurrency primitive - spawn {Coro()} [future: spawn {Expr1; Expr2}] * Async expression treated as a closure/lambda with next expression executed * immediately while the started async expression continues independently. Has no result. * spawn essentially makes any async expression an immediate expression. This also means that * it can allow async expressions / coroutines to be called within immediate functions. **/ class CExprSpawn : public CExprSubBlockBase { public: virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Concurrent_Spawn; } virtual CUTF8String GetErrorDesc() const override { return "spawn"; } }; /** * Loop flow/concurrency primitive/macro - loop {Expr1; Expr2} * Loops one or more expressions. * For safety/security it *must* have at least one async expression, `break`, or `return` * so it is less likely to have an infinite loop within a single update. **/ class CExprLoop : public CExprSubBlockBase { public: virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return _BlockExpr->FindFirstAsyncSubExpr(Program); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_Loop; } virtual CUTF8String GetErrorDesc() const override { return "loop"; } virtual bool CanFail(const CAstPackage* Package) const override { return _BlockExpr->CanFail(Package); } }; /** * defer macro - defer {Expr1; Expr2} * Defers expressions until end of current scope. Only called if encountered in flow and called in reverse order encountered. **/ class CExprDefer : public CExprSubBlockBase { public: virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return _BlockExpr->FindFirstAsyncSubExpr(Program); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_Defer; } virtual CUTF8String GetErrorDesc() const override { return "defer"; } }; /** * Control flow early exit * * loop: * if (IsEarlyExit[]): * break * DoLoopStuff() **/ class CExprBreak : public CExpressionBase { public: const CExpressionBase* _AssociatedControlFlow{nullptr}; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_Break; } virtual CUTF8String GetErrorDesc() const override { return "break"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Flow_Break; } virtual bool MayHaveAttributes() const override { return true; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; }; /** * Represents members of a class/interface/module/snippet definition node */ class CMemberDefinitions { public: CMemberDefinitions() {} CMemberDefinitions(TArray>&& Members) : _Members(Move(Members)) {} const TArray>& Members() const { return _Members; } void SetMembers(TArray>&& Members) { _Members = Move(Members); } void AppendMember(TSRef&& Member) { _Members.Add(Move(Member)); } void SetMember(TSRef&& Member, int32_t Index) { _Members[Index] = Move(Member); } void VisitMembers(SAstVisitor& Visitor) const { Visitor.VisitArray("Members", _Members); } private: TArray> _Members; }; /** * Represents a snippet in the AST. */ class CExprSnippet : public CExpressionBase, public CMemberDefinitions { public: CUTF8String _Path; CSnippet* _SemanticSnippet{ nullptr }; CExprSnippet(const CUTF8StringView& Path) : _Path(Path) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Context_Snippet; } virtual CUTF8String GetErrorDesc() const override { return "snippet"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Context_Snippet && static_cast(Other)._SemanticSnippet == _SemanticSnippet; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { VisitMembers(Visitor); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Path", _Path); } }; /** * Represents a module definition in the AST. */ class CExprModuleDefinition : public CExpressionBase, public CMemberDefinitions { public: CUTF8String _Name; CModulePart* _SemanticModule{ nullptr }; // You can get the CModule from this as well bool _bLegacyPublic = false; // To emulate legacy behavior while vmodule files are allowed CExprModuleDefinition(const CUTF8StringView& Name, EVstMappingType VstMappingType = EVstMappingType::Ast) : CExpressionBase(VstMappingType) , _Name(Name) {} UE_API CExprModuleDefinition(CModulePart& Module, TArray>&& Members); UE_API ~CExprModuleDefinition(); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Module; } virtual CUTF8String GetErrorDesc() const override { return "module definition"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_Module && static_cast(Other)._SemanticModule == _SemanticModule; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { VisitMembers(Visitor); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Name", _Name); } virtual bool CanBePathSegment(const TMacroSymbols& MacroSymbols) const override { return true; } }; /** * Represents an enum definition in the AST. */ class CExprEnumDefinition : public CExpressionBase { public: CEnumeration& _Enum; const TArray> _Members; UE_API CExprEnumDefinition(CEnumeration& Enum, TArray>&& Members, EVstMappingType = EVstMappingType::Ast); UE_API ~CExprEnumDefinition(); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Enum; } virtual CUTF8String GetErrorDesc() const override { return "enum definition"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_Enum && &static_cast(Other)._Enum == &_Enum; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.VisitArray("Members", _Members); } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; virtual bool CanBePathSegment(const TMacroSymbols& MacroSymbols) const override { return true; } }; /** * Represents both named and anonymous scoped access level definitions in the AST. * e.g. foo_scope := scoped{A,B,C} #named * foo := 42 # anonymous */ class CExprScopedAccessLevelDefinition : public CExpressionBase { public: TSRef _AccessLevelDefinition; TArray> _ScopeReferenceExprs; UE_API CExprScopedAccessLevelDefinition(TSRef& AccessLevelDefinition, EVstMappingType VstMappingType = EVstMappingType::Ast); UE_API ~CExprScopedAccessLevelDefinition(); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_ScopedAccessLevel; } virtual CUTF8String GetErrorDesc() const override { return "scoped access level"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_ScopedAccessLevel && static_cast(Other)._AccessLevelDefinition == _AccessLevelDefinition; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.VisitArray("Scopes", _ScopeReferenceExprs); } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; }; /** * Represents a interface definition in the AST. */ class CExprInterfaceDefinition : public CExpressionBase, public CMemberDefinitions { public: CInterface& _Interface; const TArray>& SuperInterfaces() const { return _SuperInterfaces; } void SetSuperInterfaces(TArray>&& SuperInterfaces) { _SuperInterfaces = SuperInterfaces; } void SetSuperInterface(TSRef&& SuperInterface, int32_t Index) { _SuperInterfaces[Index] = Move(SuperInterface); } UE_API CExprInterfaceDefinition(CInterface& Interface, TArray>&& SuperInterfaces, TArray>&& Members, EVstMappingType = EVstMappingType::Ast); CExprInterfaceDefinition(CInterface& Interface, EVstMappingType VstMappingType = EVstMappingType::Ast) : CExprInterfaceDefinition(Interface, {}, {}, VstMappingType) {} UE_API ~CExprInterfaceDefinition(); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Interface; } virtual CUTF8String GetErrorDesc() const override { return "interface definition"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_Interface && &static_cast(Other)._Interface == &_Interface; } virtual void VisitChildren(SAstVisitor& Visitor) const override { VisitMembers(Visitor); } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; virtual bool MayHaveAttributes() const override { return true; } virtual bool CanBePathSegment(const TMacroSymbols& MacroSymbols) const override { return true; } private: TArray> _SuperInterfaces; }; /** * Represents a class definition in the AST. */ class CExprClassDefinition : public CExpressionBase, public CMemberDefinitions { public: CClass& _Class; UE_API CExprClassDefinition(CClass& Class, TArray>&& SuperTypes, TArray>&& Members, EVstMappingType = EVstMappingType::Ast); CExprClassDefinition(CClass& Class, EVstMappingType VstMappingType = EVstMappingType::Ast) : CExprClassDefinition(Class, {}, {}, VstMappingType) { } UE_API ~CExprClassDefinition(); const TArray>& SuperTypes() const { return _SuperTypes; } void SetSuperTypes(TArray>&& SuperTypes) { _SuperTypes = Move(SuperTypes); } void SetSuperType(TSRef&& SuperType, int32_t Index) { _SuperTypes[Index] = Move(SuperType); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Class; } virtual CUTF8String GetErrorDesc() const override { return "class definition"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_Class && &static_cast(Other)._Class == &_Class; } virtual void VisitChildren(SAstVisitor& Visitor) const override { VisitMembers(Visitor); } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; virtual bool CanBePathSegment(const TMacroSymbols& MacroSymbols) const override { return true; } private: TArray> _SuperTypes; }; /** * Represents a data definition in the AST. */ class CExprDataDefinition : public CExprDefinition { public: const TSRef _DataMember; UE_API CExprDataDefinition(const TSRef& DataMember, TSPtr&& Element, TSPtr&& ValueDomain, TSPtr&& Value, EVstMappingType = EVstMappingType::Ast); UE_API ~CExprDataDefinition(); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Data; } virtual CUTF8String GetErrorDesc() const override { return "data definition"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_Data && &static_cast(Other)._DataMember == &_DataMember; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("DataMember", *_DataMember); } }; /** * Represents a map pair definition in the AST: (Key=>Value):Map */ class CExprIterationPairDefinition : public CExprDefinition { public: const TSRef _KeyDefinition; const TSRef _ValueDefinition; UE_API CExprIterationPairDefinition( TSRef&& KeyDefinition, TSRef&& ValueDefinition, TSPtr&& Element, TSPtr&& ValueDomain, TSPtr&& Value, EVstMappingType VstMappingType = EVstMappingType::Ast); UE_API ~CExprIterationPairDefinition(); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_IterationPair; } virtual CUTF8String GetErrorDesc() const override { return "iteration pair definition"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_IterationPair && static_cast(Other)._KeyDefinition == _KeyDefinition && static_cast(Other)._ValueDefinition == _ValueDefinition; } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("KeyDefinition", *_KeyDefinition); Visitor.VisitImmediate("ValueDefinition", *_ValueDefinition); } }; /** * Add an item to an array * The array itself is not included in the node, * instead the result destination in the code generator is used. * In the future this will be explicit in the IR, but for now it's inside code generation. **/ class CIrArrayAdd : public CExpressionBase { public: CIrArrayAdd(TSRef&& Source) : CExpressionBase(EVstMappingType::Ir) , _Source(Move(Source)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Ir_ArrayAdd; } virtual CUTF8String GetErrorDesc() const override { return "array add"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override {return _Source->FindFirstAsyncSubExpr(Program); } virtual bool CanFail(const CAstPackage* Package) const override { return _Source->CanFail(Package); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Source", _Source); } TSRef _Source; }; /// @see CIrArrayAdd class CIrMapAdd : public CExpressionBase { public: CIrMapAdd(TSRef&& Key, TSRef&& Value) : CExpressionBase(EVstMappingType::Ir) , _Key(Move(Key)) , _Value(Move(Value)) { } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Ir_MapAdd; } virtual CUTF8String GetErrorDesc() const override { return "map add"; } UE_API virtual bool operator==(const CExpressionBase&) const override; UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram&) const override; UE_API virtual bool CanFail(const CAstPackage* Package) const override; UE_API virtual void VisitChildren(SAstVisitor& Visitor) const override; TSRef _Key; TSRef _Value; }; class CIrArrayUnsafeCall : public CExpressionBase { public: CIrArrayUnsafeCall(TSRef&& Callee, TSRef&& Arguments) : _Callee(Move(Callee)) , _Argument(Move(Arguments)) { } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Ir_ArrayUnsafeCall; } virtual CUTF8String GetErrorDesc() const override { return "array unsafe call"; } UE_API virtual bool operator==(const CExpressionBase&) const override; virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return _Callee->FindFirstAsyncSubExpr(Program); } virtual bool CanFail(const CAstPackage* Package) const override { return _Callee->CanFail(Package) || _Argument->CanFail(Package); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Callee", _Callee); Visitor.Visit("Argument", _Argument); } TSRef _Callee; TSRef _Argument; }; /** * Converts a value to a dynamically typed value. Only present in the IR, not the AST. */ class CIrConvertToDynamic : public CExprUnaryOp { public: UE_API CIrConvertToDynamic(const CTypeBase* ResultType, TSRef&& Value); //~ Begin CExpressionBase interface virtual bool CanFail(const CAstPackage* Package) const override { return Operand()->CanFail(Package); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Ir_ConvertToDynamic; } virtual CUTF8String GetErrorDesc() const override { return "convert value to dynamically typed value"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Ir_ConvertToDynamic && (*this) == static_cast(Other); } //~ End CExpressionBase interface }; /** * Converts a value to a dynamically typed value. Only present in the IR, not the AST. */ class CIrConvertFromDynamic : public CExprUnaryOp { public: UE_API CIrConvertFromDynamic(const CTypeBase* ResultType, TSRef&& Value); //~ Begin CExpressionBase interface virtual bool CanFail(const CAstPackage* Package) const override { return Operand()->CanFail(Package); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Ir_ConvertFromDynamic; } virtual CUTF8String GetErrorDesc() const override { return "convert value from dynamically typed value"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Ir_ConvertFromDynamic && (*this) == static_cast(Other); } //~ End CExpressionBase interface }; /** * Bounded iteration **/ class CIrFor : public CExpressionBase { public: bool _bOutermost{ true }; bool _bGenerateResult{ true }; bool _bCanFail{ false }; const TSPtr _KeyMember; const TSRef _DataMember; const TSRef _Definition; CIrFor(const TSRef& DataMember, TSPtr&& Element, TSPtr&& ValueDomain, TSPtr&& Value) : CExpressionBase(EVstMappingType::Ir) , _KeyMember() , _DataMember(DataMember) , _Definition(TSRef::New(Move(Element), Move(ValueDomain), Move(Value), EVstMappingType::Ir)) {} CIrFor(const TSRef& KeyMember, const TSRef& DataMember, TSPtr&& Element, TSPtr&& ValueDomain, TSPtr&& Value) : CExpressionBase(EVstMappingType::Ir) , _KeyMember(KeyMember) , _DataMember(DataMember) , _Definition(TSRef::New(Move(Element), Move(ValueDomain), Move(Value), EVstMappingType::Ir)) {} void SetBody(TSPtr&& Body) { _Body = Move(Body); } UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; virtual bool CanFail(const CAstPackage* Package) const override { return _bCanFail; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Definition", _Definition); Visitor.Visit("Body", _Body); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("bGenerateResult", _bGenerateResult); if (_KeyMember) { Visitor.VisitImmediate("KeyMember", *_KeyMember); } Visitor.VisitImmediate("DataMember", *_DataMember); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Ir_For; } virtual CUTF8String GetErrorDesc() const override { return "ir_for"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; public: // The scope containing the variables used for iterating TSPtr _AssociatedScope; // Expression to evaluate for every iteration that gets past the filters step TSPtr _Body; }; /** * Wraps the innermost body of CIrFor * It wraps the code not inside the failure context of CIrFor. **/ class CIrForBody : public CExpressionBase { public: CIrForBody(TSPtr&& Body) : CExpressionBase(EVstMappingType::Ir) , _Body(Body) {} void SetBody(TSPtr&& Body) { _Body = Move(Body); } UE_API virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override; virtual bool CanFail(const CAstPackage* Package) const override { return _Body->CanFail(Package); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Body", _Body); } virtual void VisitImmediates(SAstVisitor& Visitor) const override {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Ir_ForBody; } virtual CUTF8String GetErrorDesc() const override { return "ir_for_body"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; public: // Expression to evaluate outside the failure contexts of the enclosing CIrFor TSPtr _Body; }; /** * Represents a function definition in the AST. */ class CExprFunctionDefinition : public CExprDefinition { public: const TSRef _Function; UE_API CExprFunctionDefinition(const TSRef& Function, TSPtr&& Element, TSPtr&& ValueDomain, TSPtr&& Value, EVstMappingType = EVstMappingType::Ast); UE_API ~CExprFunctionDefinition(); UE_API bool HasUserAddedPredictsEffect(const CSemanticProgram& Program) const; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Function; } virtual CUTF8String GetErrorDesc() const override { return "function definition"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_Function && static_cast(Other)._Function == _Function; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; }; /** * Represents a type alias definition in the AST. */ class CExprTypeAliasDefinition : public CExprDefinition { public: const TSRef _TypeAlias; UE_API CExprTypeAliasDefinition(const TSRef& TypeAlias, TSPtr&& Element, TSPtr&& ValueDomain, TSPtr&& Value, EVstMappingType = EVstMappingType::Ast); UE_API ~CExprTypeAliasDefinition(); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_TypeAlias; } virtual CUTF8String GetErrorDesc() const override { return "type alias definition"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_TypeAlias && static_cast(Other)._TypeAlias == _TypeAlias; } }; /** * Represents a using declaration in the AST. */ class CExprUsing : public CExpressionBase { public: // Note that not all `using` refer to a module const CModule* _Module = nullptr; TSRef _Context; CExprUsing(TSRef&& Context) : _Context(Move(Context)) {} virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Using; } virtual CUTF8String GetErrorDesc() const override { return "using"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_Using && static_cast(Other)._Module == _Module; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Context", _Context); } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; }; /** * Represents a profile block macro invocation in the AST. */ class CExprProfileBlock : public CExprSubBlockBase { public: virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Flow_ProfileBlock; } virtual CUTF8String GetErrorDesc() const override { return "profile"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Flow_ProfileBlock; } virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override { return _BlockExpr->GetResultType(Program); } virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return _BlockExpr->FindFirstAsyncSubExpr(Program); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { Visitor.Visit("UserTag", _UserTag); CExprSubBlockBase::VisitImmediates(Visitor); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("UserTag", _UserTag); CExprSubBlockBase::VisitChildren(Visitor); } TSPtr _UserTag; // Must resolve to a string type #if WITH_VERSE_BPVM const uLang::CTupleType* _ProfileLocusType; const uLang::CTupleType* _ProfileDataType; #endif }; /** * Represents a import declaration in the AST. */ class CExprImport : public CExpressionBase { public: const TSRef _ModuleAlias; const TSRef _Path; UE_API CExprImport(const TSRef& ModuleAlias, TSRef&& Path, EVstMappingType VstMappingType = EVstMappingType::Ast); UE_API ~CExprImport(); virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Import; } virtual CUTF8String GetErrorDesc() const override { return "import"; } virtual bool operator==(const CExpressionBase& Other) const override { return Other.GetNodeType() == EAstNodeType::Definition_Import; } UE_API virtual const CTypeBase* GetResultType(const CSemanticProgram& Program) const override; virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Path", _Path); } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; }; class CExprWhere : public CExpressionBase { public: CExprWhere(TSRef&& Lhs, TSPtrArray&& Rhs) : _Lhs(Move(Lhs)) , _Rhs(Move(Rhs)) { } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Where; } virtual CUTF8String GetErrorDesc() const override { return "where"; } virtual bool operator==(const CExpressionBase& Right) const override { if (!BaseCompare(*this, Right)) { return false; } const CExprWhere& RightWhere = static_cast(Right); return IsSubExprEqual(_Lhs, RightWhere._Lhs) && AreSubExprsEqual(_Rhs, RightWhere._Rhs); } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.Visit("Lhs", _Lhs); Visitor.VisitArray("Rhs", _Rhs); } const TSPtr& Lhs() const { return _Lhs; } const TSPtrArray& Rhs() const { return _Rhs; } TSPtrArray& Rhs() { return _Rhs; } void SetLhs(TSPtr NewLhs) { _Lhs = Move(NewLhs); } void SetRhs(TSPtrArray NewRhs) { _Rhs = Move(NewRhs); } private: TSPtr _Lhs; TSPtrArray _Rhs; }; class CExprVar : public CExprUnaryOp { using CExprUnaryOp::CExprUnaryOp; virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Definition_Var; } virtual bool CanFail(const CAstPackage* Package) const override { return Operand()->CanFail(Package); } virtual CUTF8String GetErrorDesc() const override { return "var"; } UE_API virtual bool operator==(const CExpressionBase& Other) const override; }; /** * Represents a named value / default value placeholder in the AST. */ class CExprMakeNamed : public CExpressionBase { public: explicit CExprMakeNamed(const CSymbol Name) : _Name(Name) { } CExprMakeNamed(const CSymbol Name, TSPtr&& NameIdentifier, TSPtr&& Argument) : _Name(Name) , _NameIdentifier(Move(NameIdentifier)) , _Value(Move(Argument)) { } const CSymbol& GetName() const { return _Name; } const TSPtr& GetNameIdentifier() const { return _NameIdentifier; } void SetNameIdentifier(TSPtr NameIdentifier) { _NameIdentifier = Move(NameIdentifier); } const TSPtr& GetValue() const { return _Value; } void SetValue(TSPtr Value) { _Value = Move(Value); } virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Invoke_MakeNamed; } virtual CUTF8String GetErrorDesc() const override { return "named"; } virtual bool operator==(const CExpressionBase& Right) const override { if (!BaseCompare(*this, Right)) { return false; } const CExprMakeNamed& RightNamedArgument = static_cast(Right); return _Name == RightNamedArgument._Name && _NameIdentifier == RightNamedArgument._NameIdentifier && IsSubExprEqual(_Value, RightNamedArgument._Value); } virtual void VisitChildren(SAstVisitor& Visitor) const override { CExpressionBase::VisitChildren(Visitor); Visitor.Visit("Value", _Value); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CExpressionBase::VisitImmediates(Visitor); Visitor.VisitImmediate("Name", _Name.AsStringView()); } virtual const CExpressionBase* FindFirstAsyncSubExpr(const CSemanticProgram& Program) const override { return _Value? _Value->FindFirstAsyncSubExpr(Program) : nullptr; } virtual bool CanFail(const CAstPackage* Package) const override { return _Value? _Value->CanFail(Package) : false; } private: CSymbol _Name; /// Optional pointer to the original identifier for the name. This can be used to preserve the AST /// information about the original source text from where the identifier came from and provide context. /// (i.e. for qualifying purposes). TSPtr _NameIdentifier; TSPtr _Value; }; /** * Represents a package in the AST. **/ class CAstPackage : public CAstNode, public CMemberDefinitions { public: CUTF8String _Name; CUTF8String _VersePath; // Verse path of the root module of this package CModulePart* _RootModule = nullptr; // Root module representing this package's Verse path TArray _Dependencies; // As specified in package settings TArray _UsedDependencies; // Dependencies actually used EVerseScope _VerseScope; // Origin/visibility of Verse code in this package EPackageRole _Role; // The role this package plays uint32_t _EffectiveVerseVersion; // The effective language version the package targets. /// This allows us to determine when a package was uploaded for a given Fortnite release version. /// It is a HACK that conditionally enables/disables behaviour in the compiler in order to /// support previous mistakes allowed to slip through in previous Verse langauge releases but /// now need to be supported for backwards compatability. /// When we can confirm that all Fortnite packages that are currently uploaded are beyond this /// version being used in all instances of the codebase, this can then be removed. uint32_t _UploadedAtFNVersion = VerseFN::UploadedAtFNVersion::Latest; // Track the number of persistent values found on a per-package basis instead of across all packages. int32_t _NumPersistentVars = 0; bool _bAllowNative; // If the native attribute is allowed bool _bTreatModulesAsImplicit; // If true, module macros in this package's source and digest will be treated as implicit bool _bAllowExperimental; // Whether to allow the use of experimental definitions in this package. CAstCompilationUnit* _CompilationUnit = nullptr; // Reverse pointer to our owner CAstPackage(const CUTF8String& Name, const CUTF8String& VersePath, const EVerseScope VerseScope, const EPackageRole Role, const uint32_t EffectiveVerseVersion, const uint32_t UploadedAtFNVersion, const bool bAllowNative, const bool bTreatDefinitionsAsImplicit, const bool bAllowExperimental) : _Name(Name) , _VersePath(VersePath) , _VerseScope(VerseScope) , _Role(Role) , _EffectiveVerseVersion(EffectiveVerseVersion) , _UploadedAtFNVersion(UploadedAtFNVersion) , _bAllowNative(bAllowNative) , _bTreatModulesAsImplicit(bTreatDefinitionsAsImplicit) , _bAllowExperimental(bAllowExperimental) {} // CAstNode interface. virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Context_Package; } virtual CUTF8String GetErrorDesc() const override { return "package"; } virtual void VisitChildren(SAstVisitor& Visitor) const override { VisitMembers(Visitor); } UE_API virtual void VisitImmediates(SAstVisitor& Visitor) const override; // Determine if the definition originates from this package or any of our dependencies UE_API bool CanSeeDefinition(const CDefinition& Definition) const; }; /** * A group of packages that must be compiled as a unit (= a strongly connected component (SCC) in the dependency graph) **/ class CAstCompilationUnit : public CAstNode { public: const TSRefArray& Packages() const { return _Packages; } void ReservePackages(int32_t Num) { _Packages.Reserve(Num); } void AppendPackage(TSRef&& Package) { _Packages.Add(Move(Package)); } UE_API EPackageRole GetRole() const; UE_API bool IsAllowNative() const; // CAstNode interface. virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Context_CompilationUnit; } virtual CUTF8String GetErrorDesc() const override { return "compilation unit"; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.VisitArray("Packages", _Packages); } private: TSRefArray _Packages; }; /** * Represents a project in the AST. **/ class CAstProject : public CAstNode { public: CUTF8String _Name; CAstProject(const CUTF8StringView& Name) : _Name(Name) {} const TSRefArray& OrderedCompilationUnits() const { return _OrderedCompilationUnits; } // Guaranteed to be sorted in order of dependency void ReserveCompilationUnits(int32_t Num) { _OrderedCompilationUnits.Reserve(Num); } void AppendCompilationUnit(TSRef&& CompilationUnit) { _OrderedCompilationUnits.Add(Move(CompilationUnit)); } UE_API const CAstPackage* FindPackageByName(const CUTF8String& PackageName) const; UE_API int32_t GetNumPackages() const; // CAstNode interface. virtual EAstNodeType GetNodeType() const override { return EAstNodeType::Context_Project; } virtual CUTF8String GetErrorDesc() const override { return "project"; } virtual void VisitChildren(SAstVisitor& Visitor) const override { Visitor.VisitArray("CompilationUnits", _OrderedCompilationUnits); } virtual void VisitImmediates(SAstVisitor& Visitor) const override { CAstNode::VisitImmediates(Visitor); Visitor.VisitImmediate("Name", _Name); } private: TSRefArray _OrderedCompilationUnits; // Guaranteed to be sorted in order of dependency }; //======================================================================================= // CAstNode Implementations //======================================================================================= template struct TAstFunctionVisitor : public SAstVisitor { FunctionType _Function; TAstFunctionVisitor(FunctionType&& Function) : _Function(ForwardArg(Function)) {} virtual void Visit(const char* FieldName, CAstNode& AstNode) override { _Function(*this, AstNode); } virtual void VisitElement(CAstNode& AstNode) override { _Function(*this, AstNode); } }; template void CAstNode::VisitChildrenLambda(FunctionType&& Function) const { TAstFunctionVisitor FunctionVisitor(ForwardArg(Function)); VisitChildren(FunctionVisitor); } //======================================================================================= // CExpressionBase Inline Methods //======================================================================================= //--------------------------------------------------------------------------------------- ULANG_FORCEINLINE bool CExpressionBase::IsSubExprEqual(const CExpressionBase* Lhs, const CExpressionBase* Rhs) { return !Lhs == !Rhs && (!Lhs || *Lhs == *Rhs); } ULANG_FORCEINLINE bool CExpressionBase::IsSubExprEqual(const CExpressionBase& Lhs, const CExpressionBase& Rhs) { return IsSubExprEqual(&Lhs, &Rhs); } //--------------------------------------------------------------------------------------- ULANG_FORCEINLINE bool CExpressionBase::IsSubExprEqual(const TSPtr& Lhs, const TSPtr& Rhs) { return IsSubExprEqual(Lhs.Get(), Rhs.Get()); } //--------------------------------------------------------------------------------------- ULANG_FORCEINLINE bool CExpressionBase::IsSubExprEqual(const TSRef& Lhs, const TSRef& Rhs) { return IsSubExprEqual(Lhs.Get(), Rhs.Get()); } //--------------------------------------------------------------------------------------- ULANG_FORCEINLINE bool CExpressionBase::AreSubExprsEqual(const TSPtrArray& Lhs, const TSPtrArray& Rhs) { if (Lhs.Num() != Rhs.Num()) { return false; } for (int32_t i = 0; i < Lhs.Num(); ++i) { if (!IsSubExprEqual(Lhs[i], Rhs[i])) { return false; } } return true; } //--------------------------------------------------------------------------------------- ULANG_FORCEINLINE bool CExpressionBase::AreSubExprsEqual(const TArray>& Lhs, const TArray>& Rhs) { if (Lhs.Num() != Rhs.Num()) { return false; } for (int32_t i = 0; i < Lhs.Num(); ++i) { if (!IsSubExprEqual(Lhs[i], Rhs[i])) { return false; } } return true; } //--------------------------------------------------------------------------------------- template struct TAsNullableTraitsOf; template struct TAsNullableTraitsOf, TOut> { using TResult = TSPtr; static TResult Cast(TSPtr X) { return X.As(); } }; template struct TAsNullableTraitsOf, TOut> { using TResult = TSPtr; static TResult Cast(TSRef X) { return X.As(); } }; template struct TAsNullableTraitsOf { using TResult = TOut*; static TResult Cast(CExpressionBase* X) { return static_cast(X); } }; template struct TAsNullableTraitsOf { using TResult = const TOut*; static TResult Cast(const CExpressionBase* X) { return static_cast(X); } }; template > typename Traits::TResult AsNullable(TIn Expr) { #define VISIT_AST_NODE_TYPE(Name, Class) \ if constexpr (std::is_same_v) \ { \ if (Expr && Expr->GetNodeType() == EAstNodeType::Name) \ { \ return Traits::Cast(Expr); \ } \ } VERSE_VISIT_AST_NODE_TYPES(VISIT_AST_NODE_TYPE) #undef VISIT_AST_NODE_TYPE return nullptr; } inline const char* AsStringLiteral(EAstNodeType Node) { #define VISIT_AST_NODE_TYPE(Name, Class) if (Node == EAstNodeType::Name) { return #Name; } VERSE_VISIT_AST_NODE_TYPES(VISIT_AST_NODE_TYPE) #undef VISIT_AST_NODE_TYPE ULANG_UNREACHABLE(); } } // namespace uLang #undef UE_API