// Copyright Epic Games, Inc. All Rights Reserved. // Implements CIrGenerator, a class that generates an IR from an Ast. // It's possible to turn this class into a nop by modifying the method ProcessAst below. // This can be done as long AstNodes are used also to represent IrNodes, and is useful // while developing the IrNode type. // The intention is that the generated IrNodes should be easier to use, both for analysis // and code generation, than the AstNodes. // However, the initial implementation only copies the AstNodes, with some care since the Ast<->Vst // must not be broken. #include "uLang/SemanticAnalyzer/IRGenerator.h" #include "uLang/Common/Algo/Cases.h" #include "uLang/Common/Algo/FindIf.h" #include "uLang/Common/Containers/SharedPointer.h" #include "uLang/Common/Containers/UniquePointer.h" #include "uLang/Diagnostics/Glitch.h" #include "uLang/Semantics/Expression.h" #include "uLang/Semantics/ScopedAccessLevelType.h" #include "uLang/Semantics/SemanticClass.h" #include "uLang/Semantics/SemanticEnumeration.h" #include "uLang/Semantics/SemanticProgram.h" #include "uLang/Semantics/SemanticInterface.h" #include "uLang/Semantics/TypeVariable.h" #include "uLang/Semantics/UnknownType.h" #include "uLang/Syntax/VstNode.h" #include namespace uLang { namespace Vst = Verse::Vst; namespace { // A simple cache used to ensure that an AstNode is mapped to the same IrNode whenever it's encounted. // Only needed for CAstPackage at since they are accessed from CSemanticModule. template class TCache { private: // A Map is probably better. TArray _FromNodes; TArray> _ToNodes; public: // Tries to find the cached value, returns null if it's not there. TSPtr TryLookup(const FromType* FromNode) const { for (int ix = 0; ix < _FromNodes.Num(); ++ix) { if (_FromNodes[ix] == FromNode) { return _ToNodes[ix]; } } return TSPtr(); } // Returns the cached value, or complain if it's not there TSPtr Lookup(const FromType* FromNode) const { if (FromNode) { TSPtr ToNode = TryLookup(FromNode); ULANG_ASSERTF(ToNode, "Failed to find object translation for AstNode"); ULANG_ASSERTF(ToNode->IsIrNode(), "Translated node isn't an IrNode"); return ToNode; } return TSPtr(); } // Add a new mapping, complaining if it's already there. void Add(FromType& FromNode, const TSRef ToNode) { if (TSPtr oldValue = TryLookup(&FromNode)) { ULANG_ASSERTF(true, oldValue.Get() == ToNode.Get() ? "Add something that is already mapped with same address" : "Add something that is already mapped with different address"); } _FromNodes.Add(&FromNode); _ToNodes.Add(ToNode); } }; template Iterator FindNamedType(Iterator First, Iterator Last, CSymbol Name) { return uLang::FindIf(First, Last, [=](const CTypeBase* Type) { const CNamedType* NamedType = Type->GetNormalType().AsNullable(); return NamedType && NamedType->GetName() == Name; }); } template Iterator FindIndexedType(Iterator First, Iterator Last, int32_t Index) { int32_t CurrentIndex = 0; for (; First != Last; ++First) { if (!(*First)->GetNormalType().template IsA()) { if (CurrentIndex == Index) { break; } ++CurrentIndex; } } return First; } template bool ElementOrderMatches(FirstIterator1 First1, LastIterator1 Last1, FirstIterator2 First2, LastIterator2 Last2) { if (Last1 - First1 != Last2 - First2) { return false; } for (; First1 != Last1; ++First1, ++First2) { if (const CNamedType* NamedType1 = (*First1)->GetNormalType().template AsNullable()) { if (const CNamedType* NamedType2 = (*First2)->GetNormalType().template AsNullable()) { if (NamedType1->GetName() != NamedType2->GetName()) { return false; } } return false; } if ((*First2)->GetNormalType().template IsA()) { return false; } } return true; } bool ElementOrderMatches(const CTupleType& Type1, const CNormalType& Type2) { const CTupleType::ElementArray& Elements1 = Type1.GetElements(); if (const CTupleType* TupleType2 = Type2.AsNullable()) { const CTupleType::ElementArray& Elements2 = TupleType2->GetElements(); return ElementOrderMatches(Elements1.begin(), Elements1.end(), Elements2.begin(), Elements2.end()); } const CTypeBase* Elements2 = &Type2; return ElementOrderMatches(Elements1.begin(), Elements1.end(), &Elements2, &Elements2 + 1); } } class CIrGeneratorImpl { public: CIrGeneratorImpl(const TSRef& Program, const TSRef& Diagnostics, SBuildParams::EWhichVM TargetVM) : _SemanticProgram(Program) , _Program(*Program) , _Diagnostics(*Diagnostics) , _Scope(Program.Get()) , _TargetVM(TargetVM) {} bool ProcessAst() { // The minimal change to disable IR is to change ProcessAst to do nothing except returning true. // The byte code will be generated from the Ast in this case. TSRef IrProject = Gen(*_Program._AstProject); _Program.SetIrProject(IrProject); return true; } TSRef Gen(const CAstProject& AstNode) { bool bCreateBuiltInPackage = _TargetVM == SBuildParams::EWhichVM::VerseVM; TGuardValue MappedVstNodeGuard(_MappedVstNode, AstNode.GetMappedVstNode()); TSRef IrNode = NewIrNode(AstNode._Name); IrNode->ReserveCompilationUnits(int32_t(bCreateBuiltInPackage) + AstNode.OrderedCompilationUnits().Num()); if (bCreateBuiltInPackage) { TSRef CompilationUnit = NewIrNode(); CompilationUnit->AppendPackage(GenPackage(*_Program._BuiltInPackage, CompilationUnit)); IrNode->AppendCompilationUnit(Move(CompilationUnit)); } for (TSRef CompilationUnit : AstNode.OrderedCompilationUnits()) { IrNode->AppendCompilationUnit(GenCompilationUnit(*CompilationUnit)); } return IrNode; } const TSRef& GetProgram() const { return _SemanticProgram; } private: void InitIrMemberDefinitions(CMemberDefinitions& IrNode, CMemberDefinitions& AstNode) { // Iterate `AstNode.Members()` using explicit indices. // `GenNode` can call `CreateCoercedOverridingFunctionDefinition` which may add to `AstNode.Members()`, // possibly invalidating iterators. Furthermore, such added functions do not // need to be visited. Computing `NumMembers` before iterating ensures this. for (int32_t Ix = 0, NumMembers = AstNode.Members().Num(); Ix < NumMembers; ++Ix) { IrNode.AppendMember(GenNode(AstNode.Members()[Ix])); } } // Package nodes must be cached since they are accessed more than once. Both from the project node and from CSemanticModule. TCache _PackageCache; TSRef GenCompilationUnit(CAstCompilationUnit& AstNode) { TGuardValue MappedVstNodeGuard(_MappedVstNode, AstNode.GetMappedVstNode()); TSRef IrNode = NewIrNode(); IrNode->ReservePackages(AstNode.Packages().Num()); for (TSRef AstPackage : AstNode.Packages()) { IrNode->AppendPackage(GenPackage(*AstPackage, IrNode)); } return IrNode; } TSRef GenPackage(CAstPackage& AstNode, CAstCompilationUnit* IrCompilationUnit) { TGuardValue MappedVstNodeGuard(_MappedVstNode, AstNode.GetMappedVstNode()); if (auto IrNode = _PackageCache.TryLookup(&AstNode)) { return IrNode.AsRef(); } TSRef IrNode = NewIrNode( AstNode._Name, AstNode._VersePath, AstNode._VerseScope, AstNode._Role, AstNode._EffectiveVerseVersion, AstNode._UploadedAtFNVersion, AstNode._bAllowNative, AstNode._bTreatModulesAsImplicit, AstNode._bAllowExperimental); _PackageCache.Add(AstNode, IrNode); IrNode->_RootModule = AstNode._RootModule; IrNode->_CompilationUnit = IrCompilationUnit; // Update the IR packages of all parents for (CModulePart* IrPart = IrNode->_RootModule; IrPart; IrPart = IrPart->GetParentScope() ? IrPart->GetParentScope()->GetModulePart() : nullptr) { ULANG_ASSERTF(IrPart->GetAstPackage() == &AstNode, "All parent module parts of a module part must belong to the same package."); IrPart->SetIrPackage(IrNode.Get()); IrPart->GetModule()->SetIrPackage(IrNode.Get()); // Only needed for the first one } for (const CAstPackage* Dependency : AstNode._Dependencies) { const CAstPackage* IrDependency = _SemanticProgram->GetIrProject()->FindPackageByName(Dependency->_Name); ULANG_ASSERT(IrDependency); IrNode->_Dependencies.Add(IrDependency); } TGuardValue ScopeGuard(_Scope, IrNode->_RootModule); InitIrMemberDefinitions(*IrNode, AstNode); return IrNode; } // Use GenNode instead. This code uses the tag to call the specific create method, // GenNode adds some general stuff that must be done for all IR nodes. // Calls ULANG_ERRORF if it detects a node type it doesn't uinderstand. TSRef MakeIrNode(CExpressionBase& AstNode) { switch (AstNode.GetNodeType()) { case EAstNodeType::External: // An (unknown) external expression - should never reach the code generator return GenExternal(static_cast(AstNode)); // Literals case EAstNodeType::Literal_Logic: // CExprLogic - Logic literal - true/false return GenLogic(static_cast(AstNode)); case EAstNodeType::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 return GenNumber(static_cast(AstNode)); case EAstNodeType::Literal_Char: // CExprChar - Character literal - 'a', '\n' return GenChar(static_cast(AstNode)); case EAstNodeType::Literal_String: // CExprString - String literal - "Hello, world!", "Line 1\nLine2" return GenString(static_cast(AstNode)); case EAstNodeType::Literal_Path: // CExprPath - Path literal - /Verse.org/Math return GenPath(static_cast(AstNode)); case EAstNodeType::Literal_Enum: // CExprEnumLiteral - Enumerator - Color.Red, Size.XXL return GenEnum(static_cast(AstNode)); case EAstNodeType::Literal_Type: // CExprType - Typedef - typedef{} return GenType(static_cast(AstNode)); case EAstNodeType::Literal_Function: // CExprFunctionLiteral - a=>b or function(a){b} return GenFunction(static_cast (AstNode)); // Identifiers case EAstNodeType::Identifier_Unresolved: // CExprIdentifierUnresolved - An existing identifier that is unresolved. It is produced by desugaring and consumed by analysis. return GenIdentifierUnresolved(static_cast (AstNode)); case EAstNodeType::Identifier_Class: // CExprIdentifierClass - Type identifier - e.g. my_type, int, color, string return GenIdentifierClass(static_cast(AstNode)); case EAstNodeType::Identifier_Module: // CExprIdentifierModule - Module name return GenIdentifierModule(static_cast(AstNode)); case EAstNodeType::Identifier_ModuleAlias: // CExprIdentifierModuleAlias - Module alias name return GenIdentifierModuleAlias(static_cast(AstNode)); case EAstNodeType::Identifier_Enum: // CExprEnumerationType - Enum name return GenIdentifierEnum(static_cast(AstNode)); case EAstNodeType::Identifier_Interface: // CExprInterfaceType - Interface name return GenIdentifierInterface(static_cast(AstNode)); case EAstNodeType::Identifier_Data: // CExprIdentifierData - Scoped data-definition (class member, local, etc.) return GenIdentifierData(static_cast(AstNode)); case EAstNodeType::Identifier_TypeAlias: // CExprIdentifierTypeAlias - Access to type alias return GenIdentifierTypeAlias(static_cast(AstNode)); case EAstNodeType::Identifier_TypeVariable: // CExprIdentifierTypeVariable - Access to a type variable return GenIdentifierTypeVariable(static_cast(AstNode)); case EAstNodeType::Identifier_Function: // CExprIdentifierFunction - Access to functions return GenIdentifierFunction(static_cast(AstNode)); case EAstNodeType::Identifier_OverloadedFunction: // CExprIdentifierOverloadedFunction - An overloaded function identifier that hasn't been resolved to a specific overload. return GenIdentifierOverloadedFunction(static_cast(AstNode)); case EAstNodeType::Identifier_Self: // CExprSelf - Access to the instance the current function is being invoked on. return GenSelf(static_cast(AstNode)); // Multi purpose syntax case EAstNodeType::Definition: // CExprDefinition - represents syntactic forms elt:domain=value, elt:domain, elt=value return GenExprDefinition(static_cast(AstNode)); // Invocations case EAstNodeType::Invoke_Invocation: // CExprInvocation - Routine call - expr1.call(expr2, expr3) return GenInvocation(static_cast(AstNode)); case EAstNodeType::Invoke_UnaryArithmetic: // CExprUnaryArithmetic - negation return GenUnaryArithmetic(static_cast(AstNode)); case EAstNodeType::Invoke_BinaryArithmetic:// CExprBinaryArithmetic - add, sub, mul, div; two operands only return GenBinaryArithmetic(static_cast(AstNode)); case EAstNodeType::Invoke_ShortCircuitAnd: // CExprShortCircuitAnd - short-circuit evaluation of logic and return GenShortCircuitAnd(static_cast(AstNode)); case EAstNodeType::Invoke_ShortCircuitOr: // CExprShortCircuitOr - short-circuit evaluation of logic or return GenShortCircuitOr(static_cast(AstNode)); case EAstNodeType::Invoke_LogicalNot: // CExprLogicalNot - logical not operator return GenLogicalNot(static_cast(AstNode)); case EAstNodeType::Invoke_Comparison: // CExprComparison - comparison operators return GenComparison(static_cast(AstNode)); case EAstNodeType::Invoke_QueryValue: // CExprQueryValue - Querying the value of a logic or option. return GenQueryValue(static_cast(AstNode)); case EAstNodeType::Invoke_MakeOption: // CExprMakeOption - Making an option value. return GenMakeOption(static_cast(AstNode)); case EAstNodeType::Invoke_MakeArray: // CExprMakeArray - Making an array value. return GenMakeArray(static_cast(AstNode)); case EAstNodeType::Invoke_MakeMap: // CExprMakeMap - Making a map value. return GenMakeMap(static_cast(AstNode)); case EAstNodeType::Invoke_MakeTuple: // CExprMakeTuple - Making a tuple value - (1, 2.0f, "three") return GenMakeTuple(static_cast(AstNode)); case EAstNodeType::Invoke_TupleElement: // CExprTupleElement - Tuple element access `TupleExpr(Idx)` return GenTupleElement(static_cast(AstNode)); case EAstNodeType::Invoke_MakeRange: // CExprMakeRange - Making a range value. return GenMakeRange(static_cast(AstNode)); case EAstNodeType::Invoke_Type: // CExprInvokeType - Invoke a type as a function on a value. return GenInvokeType(static_cast(AstNode)); case EAstNodeType::Invoke_PointerToReference: // CExprPointerToReference - Access the mutable reference behind the pointer return GenPointerToReference(static_cast(AstNode)); case EAstNodeType::Invoke_Set: // CExprSet - Evaluate operand to an l-expression. return GenSet(static_cast(AstNode)); case EAstNodeType::Invoke_NewPointer: // CExprNewPointer - Create a new pointer from an initial value. return GenNewPointer(static_cast(AstNode)); case EAstNodeType::Invoke_ReferenceToValue:// CExprReferenceToValue - Evaluates the value of an expression yielding a reference type. return GenReferenceToValue(static_cast(AstNode)); case EAstNodeType::Assignment: // CExprAssignment - Assignment operation - expr1 = expr2, expr1 := expr2, expr1 += expr2, etc. return GenAssignment(static_cast(AstNode)); // TypeFormers case EAstNodeType::Invoke_ArrayFormer: // CExprArrayTypeFormer - Invoke (at compile time) a formation of an array of another type return GenArrayTypeFormer(static_cast(AstNode)); case EAstNodeType::Invoke_GeneratorFormer: // CExprGeneratorTypeFormer - Invoke (at compile time) a formation of an generator type. return GenGeneratorTypeFormer(static_cast(AstNode)); case EAstNodeType::Invoke_MapFormer: // CExprMapTypeFormer - Invoke (at compile time) a formation of a map from a key and value type. return GenMapTypeFormer(static_cast(AstNode)); case EAstNodeType::Invoke_OptionFormer: // CExprOptionTypeFormer - Invoke (at compile time) a formation of an option of some primitive type return GenOptionTypeFormer(static_cast(AstNode)); case EAstNodeType::Invoke_Subtype: // CExprSubtype - Invoke (at compile time) a formation of a metaclass type. return GenSubtype(static_cast(AstNode)); case EAstNodeType::Invoke_TupleType: // CExprTupleType - Get or create a tuple tuple based on `tuple(type1, type2, ...)` return GenTupleType(static_cast(AstNode)); case EAstNodeType::Invoke_Arrow: // CExprArrow - Create a function type from a parameter and return type. return GenArrow(static_cast(AstNode)); case EAstNodeType::Invoke_ArchetypeInstantiation: // CExprArchetypeInstantiation - Initializer list style instantiation - Type{expr1, id=expr2, ...} return GenArchetypeInstantiation(static_cast(AstNode)); case EAstNodeType::Invoke_MakeNamed: // CExprMakeNamed return GenMakeNamed(static_cast(AstNode)); // Flow Control case EAstNodeType::Flow_CodeBlock: // CExprCodeBlock - Code block - block {expr1; expr2} return GenCodeBlock(static_cast(AstNode)); case EAstNodeType::Flow_Let: // CExprLet - - let {definition1; definition2} return GenLet(static_cast(AstNode)); case EAstNodeType::Flow_Defer: // CExprDefer - defer {expr1; expr2} return GenDefer(static_cast(AstNode)); case EAstNodeType::Flow_If: // CExprIf - Conditional with failable tests- if (Test[]) {clause1}, if (Test[]) {clause1} else {else_clause} return GenIf(static_cast(AstNode)); case EAstNodeType::Flow_Iteration: // CExprIteration - Bounded iteration over an iterable type - for(Num:Nums) {DoStuff(Num)} return GenIteration(static_cast(AstNode)); case EAstNodeType::Flow_Loop: // CExprLoop - Simple loop - loop {DoStuff()} return GenLoop(static_cast(AstNode)); case EAstNodeType::Flow_Break: // CExprBreak - Control flow early exit - loop {if (IsEarlyExit[]) {break}; DoLoopStuff()} return GenBreak(static_cast(AstNode)); case EAstNodeType::Flow_Return: // CExprReturn - Return statement - return expr return GenReturn(static_cast(AstNode)); case EAstNodeType::Flow_ProfileBlock: return GenProfileBlock(static_cast(AstNode)); // Concurrency Primitives case EAstNodeType::Concurrent_Sync: // CExprSync - sync {Coro1(); Coro2()} return GenSync(static_cast(AstNode)); case EAstNodeType::Concurrent_Rush: // CExprRush - rush {Coro1(); Coro2()} return GenRush(static_cast(AstNode)); case EAstNodeType::Concurrent_Race: // CExprRace - race {Coro1(); Coro2()} return GenRace(static_cast(AstNode)); case EAstNodeType::Concurrent_SyncIterated: // CExprSyncIterated - sync(Item:Container) {Item.Coro1(); Coro2(Item)} // No versetest trigger this return GenSyncIterated(static_cast(AstNode)); case EAstNodeType::Concurrent_RushIterated: // CExprRushIterated - rush(Item:Container) {Item.Coro1(); Coro2(Item)} // No versetest trigger this return GenRushIterated(static_cast(AstNode)); case EAstNodeType::Concurrent_RaceIterated: // CExprRaceIterated - race(Item:Container) {Item.Coro1(); Coro2(Item)} // No versetest trigger this return GenRaceIterated(static_cast(AstNode)); case EAstNodeType::Concurrent_Branch: // CExprBranch - branch {Coro1(); Coro2()} return GenBranch(static_cast(AstNode)); case EAstNodeType::Concurrent_Spawn: // CExprSpawn - spawn {Coro()} return GenSpawn(static_cast(AstNode)); // Definitions case EAstNodeType::Definition_Module: // CExprModuleDefinition return GenModuleDefinition(static_cast(AstNode)); case EAstNodeType::Definition_Enum: // CExprEnumDefinition return GenEnumDefinition(static_cast(AstNode)); case EAstNodeType::Definition_Interface: // CExprInterfaceDefinition return GenInterfaceDefinition(static_cast(AstNode)); case EAstNodeType::Definition_Class: // CExprClassDefinition return GenClassDefinition(static_cast(AstNode)); case EAstNodeType::Definition_Data: // CExprDataDefinition return GenDataDefinition(static_cast(AstNode)); case EAstNodeType::Definition_IterationPair: // CExprIterationPairDefinition return GenIterationPairDefinition(static_cast(AstNode)); case EAstNodeType::Definition_Function: // CExprFunctionDefinition return GenFunctionDefinition(static_cast(AstNode)); case EAstNodeType::Definition_TypeAlias: // CExprTypeAliasDefinition return GenTypeAliasDefinition(static_cast(AstNode)); case EAstNodeType::Definition_Using: // CExprUsing return GenExprUsing(static_cast(AstNode)); case EAstNodeType::Definition_Import: // CExprImport return GenExprImport(static_cast(AstNode)); case EAstNodeType::Definition_Where: // CExprWhere return GenExprWhere(static_cast(AstNode)); case EAstNodeType::Definition_Var: // CExprVar return GenVar(static_cast(AstNode)); case EAstNodeType::Definition_ScopedAccessLevel: // CScopedAccessLevelDefinition return GenAccessLevelDefinition(static_cast(AstNode)); case EAstNodeType::Context_Snippet: return GenExprSnippet(static_cast(AstNode)); case EAstNodeType::Error_: case EAstNodeType::Placeholder_: case EAstNodeType::PathPlusSymbol: case EAstNodeType::Identifier_BuiltInMacro: case EAstNodeType::Identifier_Local: case EAstNodeType::MacroCall: case EAstNodeType::Context_Project: case EAstNodeType::Context_CompilationUnit: case EAstNodeType::Context_Package: case EAstNodeType::Ir_For: case EAstNodeType::Ir_ForBody: case EAstNodeType::Ir_ArrayAdd: case EAstNodeType::Ir_MapAdd: case EAstNodeType::Ir_ArrayUnsafeCall: case EAstNodeType::Ir_ConvertToDynamic: case EAstNodeType::Ir_ConvertFromDynamic: default: // Use an ensure here to report an error to crash reporter, but (hopefully) not crash the entire process. ULANG_ENSUREF(false, "Tried to generate IR for unknown node type: %s", AstNode.GetErrorDesc().AsCString()); AppendGlitch(AstNode, EDiagnostic::ErrSemantic_Internal); return NewIrNode(); } } // A wrapper for MakeIrNode // There was a cache here before but there seem to be no sharing of AstNodes. // Now it does some common work for all IrNodes. TSRef GenNode(CExpressionBase& AstNode) { TGuardValue MappedVstNodeGuard( _MappedVstNode, AstNode.GetMappedVstNode() ? AstNode.GetMappedVstNode() : _MappedVstNode); TSRef IrNode = MakeIrNode(AstNode); if (!IrNode->IrGetResultType()) { IrNode->IrSetResultType(AstNode.GetResultType(*_SemanticProgram)); } return IrNode; } //----------------------------------------------------------- // Some useful utility methods TSPtr GenNode(const TSPtr& AstNode) { return AstNode ? GenNode(*AstNode) : TSPtr(); } TSRef GenNode(const TSRef& AstNode) { return GenNode(*AstNode); } TSPtr GenNode(CExpressionBase* AstNode) { return AstNode ? GenNode(*AstNode) : TSPtr(); } TArray> GenNodes(const TArray>& AstNodes) { TArray> IrNodes; for (TSRef AstNode : AstNodes) { IrNodes.Add(GenNode(*AstNode)); } return IrNodes; } TSRef NewIrQueryValue(TSRef&& Argument) { const CTypeBase* ArgumentType = Argument->GetResultType(*_SemanticProgram); TSRef QueryValue = NewIrNode(Move(Argument)); const CFunction* CalleeFunction = _SemanticProgram->_OptionQueryOp; const CFunctionType* CalleeType = SemanticTypeUtils::Instantiate(CalleeFunction->_Signature.GetFunctionType()); TSPtr Callee = NewIrNode( *CalleeFunction, CalleeType); QueryValue->SetCallee(Move(Callee)); bool bConstrained = SemanticTypeUtils::Constrain(ArgumentType, &CalleeType->GetParamsType()); ULANG_ASSERTF( bConstrained, "`ArgumentType` must be a subtype of `CalleeType->GetParamsType()`"); QueryValue->SetResolvedCalleeType(CalleeType); QueryValue->SetResultType(&CalleeType->GetReturnType()); return QueryValue; } // Creates a new code block + scope that begins with a binding of some subexpression to a temporary variable. // Used to bind a name to some subexpression with unknown effects and reference it multiple times in the rest of the block. struct STempBinding { TSRef Definition; TSRef CodeBlock; }; STempBinding BindValueToTemporaryInNewCodeBlock(TSRef&& Value, CSymbol TempName = CSymbol()) { ULANG_ASSERT(_Scope); TSRef CodeBlockScope = _Scope->CreateNestedControlScope(); TSRef CodeBlock = NewIrNode(2); CodeBlock->_AssociatedScope = CodeBlockScope; TSRef Definition = CodeBlockScope->CreateDataDefinition( TempName, Value->GetResultType(*_SemanticProgram)); CodeBlock->AppendSubExpr(NewIrNode( Definition, NewIrNode(_Program, *Definition), nullptr, Move(Value), EVstMappingType::Ir)); return STempBinding{Move(Definition), Move(CodeBlock)}; } TSRef MoveValueToNewCodeBlock(TSRef&& Value) { ULANG_ASSERT(_Scope); TSRef CodeBlockScope = _Scope->CreateNestedControlScope(); TSRef CodeBlock = NewIrNode(2); CodeBlock->_AssociatedScope = CodeBlockScope; CodeBlock->AppendSubExpr(Move(Value)); return CodeBlock; } template TSPtr WithElements(TSRef&& Expr, const CNormalType& Type, bool bBindElementsToTemporary, Function F) { ULANG_ASSERT(_Scope); if (const CTupleType* TupleType = Type.AsNullable()) { int32_t NumElements = TupleType->Num(); TArray> Elements; Elements.Reserve(NumElements); if (!bBindElementsToTemporary && Expr->GetNodeType() == EAstNodeType::Invoke_MakeTuple) { CExprMakeTuple& MakeTuple = static_cast(*Expr); for (const TSPtr& SubExpr : MakeTuple.GetSubExprs()) { Elements.Add(SubExpr); } return uLang::Invoke(F, TRangeView(Elements), TRangeView(TupleType->GetElements())); } else { auto [Definition, CodeBlock] = BindValueToTemporaryInNewCodeBlock(Move(Expr)); for (int32_t I = 0; I != NumElements; ++I) { TSRef Element = NewIrNode( NewIrNode(_Program, *Definition), I, nullptr); Element->IrSetResultType((*TupleType)[I]); Elements.Add(Move(Element)); } TSPtr Result = uLang::Invoke(F, TRangeView(Elements), TRangeView(TupleType->GetElements())); if (!Result) { return nullptr; } CodeBlock->AppendSubExpr(Move(Result)); return CodeBlock; } } const CTypeBase* ElementTypes = &Type; return uLang::Invoke(F, SingletonRangeView(Expr), SingletonRangeView(ElementTypes)); } TSPtr MaybeCoerceArrayToTuple(TSRef&& Value, const CArrayType& SourceArrayType, const CTupleType& ResultTupleType) { ULANG_ASSERT(_Scope); const CTypeBase* SourceElementType = SourceArrayType.GetElementType(); int32_t ResultNumElements = ResultTupleType.Num(); TSRef MakeTuple = NewIrNode(ResultNumElements); MakeTuple->IrSetResultType(&ResultTupleType); STempBinding SourceBinding = BindValueToTemporaryInNewCodeBlock(Move(Value)); Integer I = 0; for (const CTypeBase* ResultElementType : ResultTupleType.GetElements()) { TSRef SourceElement = NewIrNode( NewIrNode(_Program, *SourceBinding.Definition), NewIrNode(_Program, I)); SourceElement->SetResultType(SourceElementType); TSPtr CoercedElement = MaybeCoerceToType(Move(SourceElement), ResultElementType); if (!CoercedElement) { return nullptr; } MakeTuple->AppendSubExpr(Move(CoercedElement)); ++I; } SourceBinding.CodeBlock->AppendSubExpr(Move(MakeTuple)); return Move(SourceBinding.CodeBlock); } TSPtr MaybeCoerceElementsToTuple(TSRef&& Value, const CNormalType& SourceNormalType, const CTupleType& ResultTupleType) { bool bNeedsTemporary = !ElementOrderMatches(ResultTupleType, SourceNormalType); return WithElements(Move(Value), SourceNormalType, bNeedsTemporary, [&](auto&& Elements, auto&& SourceElementTypes) -> TSPtr { TSRef MakeTuple = NewIrNode(ResultTupleType.Num()); int32_t ResultIndex = 0; for (const CTypeBase* ResultElementType : ResultTupleType.GetElements()) { if (const CNamedType* ResultNamedType = ResultElementType->GetNormalType().AsNullable()) { CSymbol ResultName = ResultNamedType->GetName(); auto First = SourceElementTypes.begin(); auto Last = SourceElementTypes.end(); auto I = FindNamedType(First, Last, ResultName); if (I == Last) { TSRef CoercedElement = NewIrNode(ResultName); CoercedElement->IrSetResultType(ResultElementType); MakeTuple->AppendSubExpr(Move(CoercedElement)); } else { TSPtr Element = Elements[static_cast(I - First)]; TSPtr CoercedElement = MaybeCoerceToType( Move(Element.AsRef()), ResultElementType); if (!CoercedElement) { return nullptr; } MakeTuple->AppendSubExpr(Move(CoercedElement)); } } else { auto First = SourceElementTypes.begin(); auto Last = SourceElementTypes.end(); auto I = FindIndexedType(First, Last, ResultIndex); ULANG_ASSERTF(I != Last, "Semantic analyzer should have errored"); TSPtr Element = Elements[static_cast(I - First)]; TSPtr CoercedElement = MaybeCoerceToType( Move(Element.AsRef()), ResultElementType); if (!CoercedElement) { return nullptr; } MakeTuple->AppendSubExpr(Move(CoercedElement)); ++ResultIndex; } } MakeTuple->IrSetResultType(&ResultTupleType); return MakeTuple; }); } // Fix the case when ResultNormalType is a CNamedType but SourceNormalType isn't, and Value is a definition. // This happens in definitions like: // F1(X:int, ?Y:int = 1):int = ... // F2(X:int, ?Y:tuple(int, int) = (1, 2)):int = ... const CNormalType& GetResultNormalType(TSRef Value, const CTypeBase* ResultType, const CNormalType& SourceNormalType) { const CNormalType& ResultNormalType = SemanticTypeUtils::Canonicalize(*ResultType).GetNormalType(); if (ResultNormalType.IsA() && !SourceNormalType.IsA()) { if (Value->GetNodeType() == EAstNodeType::Definition) { // Processing a definition, check if symbols are the same CExprDefinition& Definition = static_cast(*Value); if (Definition.Element()->GetNodeType() == EAstNodeType::Identifier_Unresolved) { CExprIdentifierUnresolved& Unresolved = static_cast(*Definition.Element()); if (Unresolved._Symbol == ResultNormalType.AsChecked().GetName()) { // Symbols are the same, use the value type instead of the named type. return SemanticTypeUtils::Canonicalize(*ResultNormalType.AsChecked().GetValueType()).GetNormalType(); } } } } return ResultNormalType; } // Given a result type and an expression yielding a value in the result type's domain, return an expression that // yields the value of the provided expression in the representation of the result type. TSPtr MaybeCoerceToType(TSRef Value, const CTypeBase* ResultType) { ULANG_ASSERT(_Scope); const CTypeBase* SourceType = Value->GetResultType(*_SemanticProgram); ULANG_ENSUREF(SourceType != nullptr, "FORT-592189 - Null encountered in type coercion - Value: (0x%X) \"%s\" Result: \"%s\"", Value->GetNodeType(), Value->GetErrorDesc().AsCString(), ResultType->AsCode().AsCString()); if (SourceType == nullptr) { // BEGIN HACK added 2023/04/28 by @jason.weiler trying to capture FORT-592189 in the wild and avoid the crash AppendGlitch( *Value, EDiagnostic::ErrSemantic_Internal, CUTF8String("Internal Error: null encountered in type coercion")); return nullptr; // END HACK } const CNormalType& SourceNormalType = SemanticTypeUtils::Canonicalize(*SourceType).GetNormalType(); const CNormalType& ResultNormalType = GetResultNormalType(Value, ResultType, SourceNormalType); if (NeedsCoercion(*Value, ResultNormalType, SourceNormalType)) { if (ResultNormalType.GetKind() == Cases || SourceNormalType.GetKind() == ETypeKind::False) { TSRef CodeBlock = MoveValueToNewCodeBlock(Move(Value)); CodeBlock->AppendSubExpr(NewIrNode(_Program, false)); return CodeBlock; } else if (ResultNormalType.GetKind() == ETypeKind::False) { TSRef CodeBlock = MoveValueToNewCodeBlock(Move(Value)); CodeBlock->AppendSubExpr(NewIrNode(_Program, false)); return CodeBlock; } else if (_TargetVM != SBuildParams::EWhichVM::BPVM) { return Move(Value); } else if (ResultNormalType.GetKind() == ETypeKind::Any) { // CIrConvertToDynamic can convert any type to a dynamically typed value. return NewIrNode(ResultType, Move(Value)); } else if (SourceNormalType.GetKind() == ETypeKind::Any) { return NewIrNode(ResultType, Move(Value)); } else if (ResultNormalType.IsA() && SourceNormalType.IsA()) { const CArrayType& ResultArrayType = ResultNormalType.AsChecked(); const CTupleType& SourceTupleType = SourceNormalType.AsChecked(); int32_t N = SourceTupleType.Num(); TSRef MakeArray = NewIrNode(N); MakeArray->IrSetResultType(ResultType); STempBinding SourceBinding = BindValueToTemporaryInNewCodeBlock(Move(Value)); int32_t I = 0; for (const CTypeBase* SourceElementType : SourceTupleType.GetElements()) { TSRef SourceElement = NewIrNode( NewIrNode(_Program, *SourceBinding.Definition), I, nullptr); SourceElement->SetResultType(SourceElementType); TSPtr CoercedElement = MaybeCoerceToType(Move(SourceElement), ResultArrayType.GetElementType()); if (!CoercedElement) { return nullptr; } MakeArray->AppendSubExpr(Move(CoercedElement)); ++I; } SourceBinding.CodeBlock->AppendSubExpr(Move(MakeArray)); return Move(SourceBinding.CodeBlock); } else if (const CTupleType* ResultTupleType = ResultNormalType.AsNullable()) { if (const CArrayType* SourceArrayType = SourceNormalType.AsNullable(); SourceArrayType && ResultTupleType->GetFirstNamedIndex() == ResultTupleType->Num()) { return MaybeCoerceArrayToTuple(Move(Value), *SourceArrayType, *ResultTupleType); } else { return MaybeCoerceElementsToTuple(Move(Value), SourceNormalType, *ResultTupleType); } } else if (ResultNormalType.IsA() && SourceNormalType.IsA()) { const CNamedType& ResultNamedType = ResultNormalType.AsChecked(); ULANG_ASSERTF(ResultNamedType.HasValue(), "Semantic analyzer should have errored"); if (SourceNormalType.AsChecked().Num() != 0) { // Should never happen, but have happend before so might again. AppendGlitch( *Value, EDiagnostic::ErrSemantic_Unimplemented, CUTF8String("Unsupported usage of named type")); } TSRef CoercedValue = NewIrNode(ResultNamedType.GetName()); CoercedValue->IrSetResultType(ResultType); return Move(CoercedValue); } else if (ResultNormalType.IsA() && SourceNormalType.IsA()) { TSRef MakeRationalFromInt = NewIrNode(Move(Value)); const CFunction* MakeRationalFromIntFunction = _Program._MakeRationalFromInt; const CFunctionType* MakeRationalFromIntFunctionType = SemanticTypeUtils::Instantiate(MakeRationalFromIntFunction->_Signature.GetFunctionType()); MakeRationalFromInt->SetCallee(TSRef::New(*MakeRationalFromIntFunction, MakeRationalFromIntFunctionType)); bool bConstrained = SemanticTypeUtils::Constrain(SourceType, &MakeRationalFromIntFunctionType->GetParamsType()); ULANG_ASSERTF( bConstrained, "`DivArgumentType` must be a subtype of `DivFunctionType->GetParamsType()`"); MakeRationalFromInt->SetResolvedCalleeType(MakeRationalFromIntFunctionType); MakeRationalFromInt->SetResultType(&MakeRationalFromIntFunctionType->GetReturnType()); return Move(MakeRationalFromInt); } else if (ResultNormalType.GetKind() == SourceNormalType.GetKind()) { // If coercing from some parametric type to another parametric type of the same kind, the coercion // distributes to the type parameter. const ETypeKind CommonKind = ResultNormalType.GetKind(); if (CommonKind == ETypeKind::Option) { // If coercing expr:?t to ?u, translate the coercion to: // let(Option:=expr) in option{u(Option?)}. const COptionType& ResultOptionType = ResultNormalType.AsChecked(); STempBinding SourceBinding = BindValueToTemporaryInNewCodeBlock(Move(Value)); TSRef QueryValue = NewIrQueryValue( NewIrNode(_Program, *SourceBinding.Definition)); TSPtr CoercedValue = MaybeCoerceToType(Move(QueryValue), ResultOptionType.GetValueType()); if (!CoercedValue) { return nullptr; } SourceBinding.CodeBlock->AppendSubExpr(NewIrNode( &ResultOptionType, Move(CoercedValue))); return Move(SourceBinding.CodeBlock); } else if (CommonKind == ETypeKind::Array) { // If coercing expr:[]t to []u, translate the coercion to: // { Array:=expr; for(Item:Array) in u(Item) } // expr must be evaluated outside of for otherwise all failures will be translated to empty arrays const CArrayType& ResultArrayType = ResultNormalType.AsChecked(); const CArrayType& SourceArrayType = SourceNormalType.AsChecked(); STempBinding SourceBinding = BindValueToTemporaryInNewCodeBlock(Move(Value), _Program.GetSymbols()->AddChecked("Array", true)); TSRef ForScope = SourceBinding.CodeBlock->_AssociatedScope->CreateNestedControlScope(); TSRef ElementDefinition = ForScope->CreateDataDefinition(_Program.GetSymbols()->AddChecked("Item", true)); ElementDefinition->SetType(SourceArrayType.GetElementType()); TSRef For = NewIrNode( ElementDefinition, NewIrNode(_Program, *ElementDefinition), NewIrNode(_Program, *SourceBinding.Definition), nullptr); For->_bGenerateResult = true; For->_AssociatedScope = Move(ForScope); For->IrSetResultType(ResultType); TSRef Element = NewIrNode(_Program, *ElementDefinition); TSPtr CoercedElement = MaybeCoerceToType( Move(Element.As()), ResultArrayType.GetElementType()); if (!CoercedElement) { return nullptr; } For->SetBody(NewIrNode(NewIrNode(Move(CoercedElement.AsRef())))); SourceBinding.CodeBlock->AppendSubExpr(Move(For)); SourceBinding.CodeBlock->IrSetResultType(&ResultArrayType); return Move(SourceBinding.CodeBlock); } else if (CommonKind == ETypeKind::Generator) { // If coercing expr:generator(t) to generator(u), translate the coercion to: // { Array:=expr; for(Item:Array) in u(Item) } // expr must be evaluated outside of for otherwise all failures will be translated to empty arrays const CGeneratorType& ResultGeneratorType = ResultNormalType.AsChecked(); const CGeneratorType& SourceGeneratorType = SourceNormalType.AsChecked(); STempBinding SourceBinding = BindValueToTemporaryInNewCodeBlock(Move(Value), _Program.GetSymbols()->AddChecked("Generator", true)); TSRef ForScope = SourceBinding.CodeBlock->_AssociatedScope->CreateNestedControlScope(); TSRef ElementDefinition = ForScope->CreateDataDefinition(_Program.GetSymbols()->AddChecked("Item", true)); ElementDefinition->SetType(SourceGeneratorType.GetElementType()); TSRef For = NewIrNode( ElementDefinition, NewIrNode(_Program, *ElementDefinition), NewIrNode(_Program, *SourceBinding.Definition), nullptr); For->_bGenerateResult = true; For->_AssociatedScope = Move(ForScope); For->IrSetResultType(ResultType); TSRef Element = NewIrNode(_Program, *ElementDefinition); TSPtr CoercedElement = MaybeCoerceToType( Move(Element.As()), ResultGeneratorType.GetElementType()); if (!CoercedElement) { return nullptr; } For->SetBody(NewIrNode(NewIrNode(Move(CoercedElement.AsRef())))); SourceBinding.CodeBlock->AppendSubExpr(Move(For)); SourceBinding.CodeBlock->IrSetResultType(&ResultGeneratorType); return Move(SourceBinding.CodeBlock); } else if (CommonKind == ETypeKind::Map) { const CMapType& ResultMapType = ResultNormalType.AsChecked(); const CMapType& SourceMapType = SourceNormalType.AsChecked(); STempBinding SourceBinding = BindValueToTemporaryInNewCodeBlock(Move(Value), _Program.GetSymbols()->AddChecked("Map", true)); TSRef ForScope = SourceBinding.CodeBlock->_AssociatedScope->CreateNestedControlScope(); TSRef MapKeyDefinition = ForScope->CreateDataDefinition(_Program.GetSymbols()->AddChecked("Key", true)); MapKeyDefinition->SetType(SourceMapType.GetKeyType()); TSRef MapValueDefinition = ForScope->CreateDataDefinition(_Program.GetSymbols()->AddChecked("Value", true)); MapValueDefinition->SetType(SourceMapType.GetValueType()); TSRef For = NewIrNode( MapKeyDefinition, MapValueDefinition, nullptr, NewIrNode(_Program, *SourceBinding.Definition), nullptr); For->_bGenerateResult = true; For->_AssociatedScope = Move(ForScope); For->IrSetResultType(&ResultMapType); TSRef MapKey = NewIrNode(_Program, *MapKeyDefinition); TSPtr CoercedMapKey = MaybeCoerceToType( Move(MapKey.As()), ResultMapType.GetKeyType()); if (!CoercedMapKey) { return nullptr; } TSRef MapValue = NewIrNode(_Program, *MapValueDefinition); TSPtr CoercedMapValue = MaybeCoerceToType( Move(MapValue.As()), ResultMapType.GetValueType()); if (!CoercedMapValue) { return nullptr; } For->SetBody(NewIrNode(NewIrNode(Move(CoercedMapKey.AsRef()), Move(CoercedMapValue.AsRef())))); SourceBinding.CodeBlock->AppendSubExpr(Move(For)); SourceBinding.CodeBlock->IrSetResultType(&ResultMapType); return Move(SourceBinding.CodeBlock); } else if (CommonKind == ETypeKind::Function && Value->GetNodeType() == EAstNodeType::Identifier_Function) { CExprIdentifierFunction& Identifier = static_cast(*Value); const CFunction& Function = Identifier._Function; const CFunctionType& ResultFunctionType = ResultNormalType.AsChecked(); if (CScope* CoercedFunctionScope = GetScopeForCoercedFunction(Function)) { std::size_t CoercedFunctionId = 0; const CFunction* CoercedFunction = FindCoercedFunction(Function, ResultFunctionType, CoercedFunctionId); if (!CoercedFunction) { CSymbol CoercedFunctionName = _Program.GetSymbols()->AddChecked(CUTF8String("%s%zu", Function.GetName().AsCString(), CoercedFunctionId), true); TSPtr CoercedFunctionDefinition = MaybeCreateCoercedFunctionDefinition( CoercedFunctionName, Function, *CoercedFunctionScope, {}, ResultFunctionType); if (!CoercedFunctionDefinition) { return nullptr; } CoercedFunction = CoercedFunctionDefinition->_Function.Get(); _CoercedFunctions.Add({&Function, &ResultFunctionType, CoercedFunction}); _Scope->GetPackage()->AppendMember(Move(CoercedFunctionDefinition.AsRef())); } return NewIrNode( *CoercedFunction, &ResultFunctionType, Identifier.TakeContext(), Identifier.TakeQualifier()); } } } return nullptr; } return Move(Value); } void AppendCoerceToTypeGlitch(const CAstNode& Node, const CTypeBase& SourceType, const CTypeBase& ResultType) { AppendGlitch( Node, EDiagnostic::ErrSemantic_Unimplemented, CUTF8String( "Using a value of type %s as a value of type %s is not yet implemented.", SourceType.AsCode().AsCString(), ResultType.AsCode().AsCString())); } TSRef CoerceToType(TSRef&& IrNode, const CTypeBase* ResultType) { if (TSPtr Result = MaybeCoerceToType(IrNode, ResultType)) { return Move(Result.AsRef()); } const CTypeBase* SourceType = IrNode->GetResultType(*_SemanticProgram); AppendCoerceToTypeGlitch(*IrNode, *SourceType, *ResultType); // To prevent redundant coercion errors from being produced, replace the subexpression // with an error node. TSRef Error = NewIrNode(); Error->AppendChild(IrNode); return Error; } TSPtr CoerceToType(TSPtr&& IrNode, const CTypeBase* ResultType) { if (IrNode) { return CoerceToType(Move(IrNode.AsRef()), ResultType); } else { return nullptr; } } TSPtr CreateCoercedOverridingFunctionDefinition( CFunction& Function, const TArray& CoercedTypes, const CFunctionType& CoercedType) { if (TSPtr Result = MaybeCreateCoercedFunctionDefinition( Function.GetName(), Function, Function._EnclosingScope, CoercedTypes, CoercedType)) { Function.MarkCoercedOverride(); return Result; } const CFunctionType* Type = Function._Signature.GetFunctionType(); AppendCoerceToTypeGlitch(*Function.GetIrNode(), *Type, CoercedType); return nullptr; } CSymbol MakeGeneratedName(CSymbol OriginalName) { return _Program.GetSymbols()->AddChecked(OriginalName.AsStringView(), true); } TSPtr MaybeCreateCoercedFunctionDefinition( CSymbol CoercedFunctionName, const CFunction& Function, CScope& Scope, const TArray& CoercedTypes, const CFunctionType& CoercedType) { const CFunctionType* Type = Function._Signature.GetFunctionType(); TSRef CoercedFunction = Scope.CreateFunction(MakeGeneratedName(CoercedFunctionName)); CoercedFunction->MarkCoercion(Function); CSymbol ArgumentName = _Program.GetSymbols()->AddChecked("Argument", true); TSRef ArgumentDefinition = CoercedFunction->CreateDataDefinition(ArgumentName); SSignature CoercedSignature(CoercedType, {ArgumentDefinition.Get()}); CoercedFunction->SetSignature(Move(CoercedSignature), Function.GetSignatureRevision()); ArgumentDefinition->SetType(&CoercedType.GetParamsType()); CExprInvocation::EBracketingStyle BracketingStyle = Type->GetEffects()[EEffect::decides]? CExprInvocation::EBracketingStyle::SquareBrackets : CExprInvocation::EBracketingStyle::Parentheses; TGuardValue MappedVstNodeGuard(_MappedVstNode, Function.GetIrNode()->GetMappedVstNode()); TSRef ArgumentExpr = NewIrNode( _Program, *ArgumentDefinition); TSPtr CoercedArgumentExpr = Move(ArgumentExpr); for (auto First = CoercedTypes.begin(), Last = CoercedTypes.end(); First != Last; --Last) { auto I = Last; --I; CoercedArgumentExpr = MaybeCoerceToType( Move(CoercedArgumentExpr.AsRef()), &(*I)->GetParamsType()); if (!CoercedArgumentExpr) { return nullptr; } } CoercedArgumentExpr = MaybeCoerceToType( Move(CoercedArgumentExpr.AsRef()), &Type->GetParamsType()); if (!CoercedArgumentExpr) { return nullptr; } TSRef Invocation = NewIrNode( BracketingStyle, NewIrNode(Function, Type), Move(CoercedArgumentExpr.AsRef())); Invocation->SetResultType(&Type->GetReturnType()); Invocation->SetResolvedCalleeType(Type); TSPtr CoercedFunctionBody = Move(Invocation); for (auto First = CoercedTypes.begin(), Last = CoercedTypes.end(); First != Last; ++First) { CoercedFunctionBody = MaybeCoerceToType( Move(CoercedFunctionBody.AsRef()), &(*First)->GetReturnType()); if (!CoercedFunctionBody) { return nullptr; } } CoercedFunctionBody = MaybeCoerceToType( Move(CoercedFunctionBody.AsRef()), &CoercedType.GetReturnType()); if (!CoercedFunctionBody) { return nullptr; } TSRef CoercedFunctionDefinition = NewIrNode( CoercedFunction, nullptr, nullptr, Move(CoercedFunctionBody), EVstMappingType::Ir); CoercedFunction->SetIrNode(CoercedFunctionDefinition.Get()); return CoercedFunctionDefinition; } CScope* GetScopeForCoercedFunction(const CFunction& Function) { if (Function.IsInstanceMember()) { // The coerced function of an instance member must be added to the // same class. This is only possible for classes in non-external packages. if (Function._EnclosingScope.GetPackage()->_Role == EPackageRole::External) { return nullptr; } return &Function._EnclosingScope; } else { // The coerced function of a non-instance member can be added to the // module of the current scope. return _Scope->GetModulePart(); } } // Determine whether coercion is necessary between the runtime representations of two types. bool NeedsCoercion(const CNormalType& ResultType, const CNormalType& SourceType) { if (ResultType.GetKind() == Cases && SourceType.GetKind() != Cases) { return true; } else if (ResultType.GetKind() != Cases && SourceType.GetKind() == ETypeKind::False) { return true; } else if (ResultType.GetKind() == ETypeKind::False && SourceType.GetKind() != Cases) { return true; } else if (ResultType.IsA() && SourceType.IsA()) { return true; } else if (ResultType.IsA() && SourceType.IsA()) { return true; } else if (ResultType.GetKind() != Cases && SourceType.IsA()) { return true; } else if (ResultType.IsA() && SourceType.GetKind() != Cases) { return true; } else if (ResultType.IsA() && SourceType.IsA()) { return true; } else if (ResultType.GetKind() == SourceType.GetKind()) { const ETypeKind CommonKind = ResultType.GetKind(); if (CommonKind == ETypeKind::Array) { const CArrayType& ResultArrayType = ResultType.AsChecked(); const CArrayType& SourceArrayType = SourceType.AsChecked(); return NeedsCoercion(ResultArrayType.GetElementType(), SourceArrayType.GetElementType()); } else if (CommonKind == ETypeKind::Generator) { const CGeneratorType& ResultGeneratorType = ResultType.AsChecked(); const CGeneratorType& SourceGeneratorType = SourceType.AsChecked(); return NeedsCoercion(ResultGeneratorType.GetElementType(), SourceGeneratorType.GetElementType()); } else if (CommonKind == ETypeKind::Map) { const CMapType& ResultMapType = ResultType.AsChecked(); const CMapType& SourceMapType = SourceType.AsChecked(); return NeedsCoercion(ResultMapType.GetKeyType(), SourceMapType.GetKeyType()) || NeedsCoercion(ResultMapType.GetValueType(), SourceMapType.GetValueType()); } else if (CommonKind == ETypeKind::Option) { const COptionType& ResultOptionType = ResultType.AsChecked(); const COptionType& SourceOptionType = SourceType.AsChecked(); return NeedsCoercion(ResultOptionType.GetValueType(), SourceOptionType.GetValueType()); } else if (CommonKind == ETypeKind::Tuple) { const CTupleType& ResultTupleType = ResultType.AsChecked(); const CTupleType& SourceTupleType = SourceType.AsChecked(); int32_t ResultNumElements = ResultTupleType.Num(); if (ResultNumElements != SourceTupleType.Num()) { return true; } for (int32_t ElementIndex = 0; ElementIndex != ResultNumElements; ++ElementIndex) { const CNormalType& ResultElementType = ResultTupleType[ElementIndex]->GetNormalType(); const CNormalType& SourceElementType = SourceTupleType[ElementIndex]->GetNormalType(); if (const CNamedType* ResultNamedType = ResultElementType.AsNullable()) { if (const CNamedType* SourceNamedType = SourceElementType.AsNullable()) { if (ResultNamedType->GetName() != SourceNamedType->GetName()) { return true; } if (NeedsCoercion(ResultNamedType->GetValueType(), SourceNamedType->GetValueType())) { return true; } } else { return true; } } else if (SourceElementType.IsA()) { return true; } else if (NeedsCoercion(ResultElementType, SourceElementType)) { return true; } } return false; } else if (CommonKind == ETypeKind::Function) { const CFunctionType& ResultFunctionType = ResultType.AsChecked(); const CFunctionType& SourceFunctionType = SourceType.AsChecked(); if (SourceFunctionType.ImplicitlySpecialized()) { return false; } if (NeedsCoercion(&SourceFunctionType.GetParamsType(), &ResultFunctionType.GetParamsType())) { return true; } if (NeedsCoercion(&ResultFunctionType.GetReturnType(), &SourceFunctionType.GetReturnType())) { return true; } if (ResultFunctionType.GetEffects() != SourceFunctionType.GetEffects()) { return true; } return false; } else if (CommonKind == ETypeKind::Named) { const CNamedType& ResultNamedType = ResultType.AsChecked(); const CNamedType& SourceNamedType = SourceType.AsChecked(); if (ResultNamedType.GetName() != SourceNamedType.GetName()) { return true; } if (NeedsCoercion(ResultNamedType.GetValueType(), SourceNamedType.GetValueType())) { return true; } return false; } } else { const bool bResultIsDynamicallyTyped = ResultType.GetKind() == ETypeKind::Any; const bool bSourceIsDynamicallyTyped = SourceType.GetKind() == ETypeKind::Any; return bResultIsDynamicallyTyped != bSourceIsDynamicallyTyped; } return false; } bool NeedsCoercion(const CTypeBase* ResultType, const CTypeBase* SourceType) { return NeedsCoercion(ResultType->GetNormalType(), SourceType->GetNormalType()); } bool NeedsCoercion(const CExpressionBase& Value, const CNormalType& ResultType, const CNormalType& SourceType) { if (Value.GetNodeType() == EAstNodeType::Identifier_Function) { const CExprIdentifierFunction& Identifier = static_cast(Value); if (!Identifier.HasAttributeClass(_Program._constructorClass, _Program) && Identifier._Function.HasAttributeClass(_Program._constructorClass, _Program)) { return true; } } if (NeedsCoercion(ResultType, SourceType)) { return true; } return false; } CExprMacroCall::CClause CreateClause(const CExprMacroCall::CClause& Clause) { return CExprMacroCall::CClause(Clause.Tag(), Clause.Form(), GenNodes(Clause.Exprs())); } //------------------------------------------------------------- // The copy code TSRefGenExternal(CExprExternal& AstNode) { TSRefIrNode = NewIrNode(_Program); return IrNode; } TSRefGenLogic(CExprLogic& AstNode) { TSRefIrNode = NewIrNode(_Program, AstNode._Value); return IrNode; } TSRefGenNumber(CExprNumber& AstNode) { TSRef IrNode = AstNode.IsFloat() ? NewIrNode(_Program, AstNode.GetFloatValue()) : NewIrNode(_Program, AstNode.GetIntValue()); return IrNode; } TSRefGenChar(CExprChar& AstNode) { TSRef IrNode = NewIrNode(AstNode._CodePoint, AstNode._Type); return IrNode; } TSRefGenString(CExprString& AstNode) { TSRef IrNode = NewIrNode(AstNode._String); return IrNode; } TSRefGenPath(CExprPath& AstNode) { TSRef IrNode = NewIrNode(AstNode._Path); return IrNode; } TSRefGenEnum(CExprEnumLiteral& AstNode) { TSRef IrNode = NewIrNode(AstNode._Enumerator, GenNode(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } TSRefGenType(CExprType& AstNode) { TSRef AbstractValue = AstNode._AbstractValue; // We don't want to gen the abstract value because it could be in an inconsistent state for `type{ _X:int where ... }` and it's not needed for lowering to BP bytecode. TSRef IrNode = NewIrNode(Move(AbstractValue), *AstNode.GetTypeType()); return IrNode; } TSRefGenFunction(CExprFunctionLiteral& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode.Domain()), GenNode(*AstNode.Range())); return IrNode; } TSPtr GenNodeUnlessModule(const TSPtr& Context) { // Module id was used during analysis, but should not be left for code generation. if (!Context || Context->GetNodeType() == Cases) { return TSPtr(); } else { return GenNode(Context); } } TSRef GenIdentifierUnresolved(CExprIdentifierUnresolved& AstNode) { TSRef IrNode = NewIrNode( AstNode._Symbol, GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } TSRefGenIdentifierClass(CExprIdentifierClass& AstNode) { TSRef IrNode = NewIrNode(AstNode.GetTypeType(_Program), GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } TSRefGenIdentifierModule(CExprIdentifierModule& AstNode) { TSRef IrNode = NewIrNode(AstNode.GetModule(_Program), GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } TSRefGenIdentifierModuleAlias(CExprIdentifierModuleAlias& AstNode) { TSRef IrNode = NewIrNode(AstNode._ModuleAlias, GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } TSRefGenIdentifierEnum(CExprEnumerationType& AstNode) { TSRef IrNode = NewIrNode(AstNode.GetTypeType(_Program), GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } TSRefGenIdentifierInterface(CExprInterfaceType& AstNode) { TSRef IrNode = NewIrNode(AstNode.GetTypeType(_Program), GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } TSRef GenIdentifierData(CExprIdentifierData& AstNode) { // If a data definition is instantiated as part of a parametric type, lower it to its prototype definition+generic type. const CTypeBase* ResultType = AstNode.GetResultType(_Program); const CDataDefinition& OverriddenPrototypeDefinition = *AstNode._DataDefinition.GetBaseOverriddenDefinition().GetPrototypeDefinition(); TSPtr IrContext; if (TSPtr Context = AstNode.Context()) { IrContext = GenNodeUnlessModule(Move(Context.AsRef())); } TSRef IrNode = NewIrNode( _Program, OverriddenPrototypeDefinition, Move(IrContext), GenNode(AstNode.Qualifier())); return CoerceToType(Move(IrNode.As()), ResultType); } TSRefGenIdentifierTypeAlias(CExprIdentifierTypeAlias& AstNode) { TSRef IrNode = NewIrNode(AstNode._TypeAlias, GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } TSRef GenIdentifierTypeVariable(CExprIdentifierTypeVariable& AstNode) { TSRef IrNode = NewIrNode(AstNode._TypeVariable, GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier())); return IrNode; } // Get all type variables that were instantiated for this function // identifier. This includes both type variables quantified by the function // directly, as well as type variables quantified by any containing class or // interface (perhaps implicitly via the rewriting of `:type`). Note that // repeated instantiation, e.g. // @code // class1(t:type) := class: // Method(:u where u:subtype(t)):void // @endcode // results in merging the flow types generated for `t` (via `Merge`) into // the flow types generated for `u` - i.e., no repeated remapping by callers // of `GetInstantiatedTypeVariables` need occur. TArray GetInstantiatedTypeVariables(const CExprIdentifierFunction& AstNode) { TArray InstTypeVariables = AstNode._InstantiatedTypeVariables; CScope& EnclosingScope = AstNode._Function._EnclosingScope; if (EnclosingScope.GetKind() == CScope::EKind::Class) { for (const STypeVariableSubstitution Substitution : static_cast(EnclosingScope)._TypeVariableSubstitutions) { InstTypeVariables.Emplace(Substitution._NegativeType, Substitution._PositiveType); } } else if (EnclosingScope.GetKind() == CScope::EKind::Interface) { for (const STypeVariableSubstitution Substitution : static_cast(EnclosingScope)._TypeVariableSubstitutions) { InstTypeVariables.Emplace(Substitution._NegativeType, Substitution._PositiveType); } } return InstTypeVariables; } TSRef GenIdentifierFunction(CExprIdentifierFunction& AstNode) { // If the function is instantiated as part of a parametric type, lower it to its prototype definition+generic type. TArray InstTypeVariables = GetInstantiatedTypeVariables(AstNode); const CTypeBase* ResultType = &SemanticTypeUtils::AsPositive(*AstNode.GetResultType(_Program), InstTypeVariables); const CFunction& PrototypeFunction = *AstNode._Function.GetPrototypeDefinition(); const CFunctionType* SourceType = PrototypeFunction._Signature.GetFunctionType(); TSPtr IrContext; if (TSPtr Context = AstNode.Context()) { IrContext = GenNodeUnlessModule(Move(Context.AsRef())); } TSRef IrNode = NewIrNode( PrototypeFunction, TArray{}, SourceType, AstNode._ConstructorNegativeReturnType, Move(IrContext), GenNode(AstNode.Qualifier()), AstNode._bSuperQualified); return CoerceToType(Move(IrNode), ResultType); } TSRef GenIdentifierOverloadedFunction(CExprIdentifierOverloadedFunction& AstNode) { TArray OverloadedFunctions(AstNode._FunctionOverloads); TSRef IrNode = NewIrNode( Move(OverloadedFunctions), AstNode._bConstructor, AstNode._Symbol, AstNode._TypeOverload, GenNodeUnlessModule(AstNode.Context()), GenNode(AstNode.Qualifier()), AstNode.GetResultType(_Program)); return IrNode; } TSRef GenSelf(CExprSelf& AstNode) { TSRef IrNode = NewIrNode(AstNode.GetResultType(_Program), GenNode(AstNode.Qualifier())); return IrNode; } TSRef GenExprDefinition(CExprDefinition& AstNode) { TSRef IrNode = NewIrNode(GenNode(AstNode.Element()), GenNode(AstNode.ValueDomain()), GenNode(AstNode.Value())); IrNode->SetName(AstNode.GetName()); if (CDataDefinition* FunctionParamDefinition = FindFunctionParamDefinition(AstNode)) { FunctionParamDefinition->SetIrNode(IrNode.Get()); } return IrNode; } TSRef GenInvocation(CExprInvocation& AstNode, const CExprIdentifierFunction& AstIdentifierFunction, TSPtr&& IrContext) { const CFunction& PrototypeCalleeFunction = *AstIdentifierFunction._Function.GetPrototypeDefinition(); // Native methods are required to implement the prototype of the base // overridden definition - i.e., the type erased form of the root-most // method signature. const CFunctionType* PrototypeCalleeType = AstIdentifierFunction._Function.IsNative() ? AstIdentifierFunction._Function.GetBaseOverriddenDefinition().GetPrototypeDefinition()->_Signature.GetFunctionType() : AstIdentifierFunction._Function.GetPrototypeDefinition()->_Signature.GetFunctionType(); TSPtr IrQualifier = GenNode(AstIdentifierFunction.Qualifier()); TSRef IrCallee = NewIrNode( PrototypeCalleeFunction, TArray{}, PrototypeCalleeType, AstIdentifierFunction._ConstructorNegativeReturnType, Move(IrContext), Move(IrQualifier), AstIdentifierFunction._bSuperQualified); TSRef IrArgument = GenNode(*AstNode.GetArgument()); const CFunctionType& InstCalleeType = AstIdentifierFunction.GetResultType(_Program)->GetNormalType().AsChecked(); const CTypeBase& InstParamsType = SemanticTypeUtils::AsPositive( InstCalleeType.GetParamsType(), GetInstantiatedTypeVariables(AstIdentifierFunction)); IrArgument = CoerceToType(Move(IrArgument), &InstParamsType); if (!InstCalleeType.ImplicitlySpecialized()) { // Attempt to coerce the argument to the generalized function parameter // type, result to expected result type first. Failing this, coerce the // callee to the instantiated callee type. Values of function type can // only be coerced if the value is the function identifier (to avoid // generating a closure), so either options may fail - try both to // ensure more programs compile. if (TSPtr CoercedIrArgument = MaybeCoerceToType( IrArgument, &PrototypeCalleeType->GetParamsType())) { TSRef IrNode = NewIrNode( AstNode._CallsiteBracketStyle, IrCallee, // Don't move IrCallee since if the ResultType coercion fails, we'll use it below. Move(CoercedIrArgument.AsRef())); IrNode->SetResolvedCalleeType(AstNode.GetResolvedCalleeType()); IrNode->SetResultType(&PrototypeCalleeType->GetReturnType()); const CTypeBase* ResultType = AstNode.GetResultType(_Program); if (TSPtr CoercedIrNode = MaybeCoerceToType( IrNode, ResultType)) { // `MaybeCoerceToType` will ensure the low representation of // the types are the same. Explicitly set the type to // `ResultType` to preserve the high representation // (important for digest generation). CoercedIrNode->IrSetResultType(ResultType); return CoercedIrNode.AsRef(); } } // Coercion of the argument and return types failed. Attempt coercion of the callee. IrCallee = CoerceToType(Move(IrCallee), &InstCalleeType); } TSRef IrNode = NewIrNode( AstNode._CallsiteBracketStyle, Move(IrCallee), Move(IrArgument)); IrNode->SetResolvedCalleeType(AstNode.GetResolvedCalleeType()); IrNode->SetResultType(AstNode.GetResultType(_Program)); return IrNode; } TSRef GenInvocation(CExprInvocation& AstNode, const CExprIdentifierFunction& AstIdentifierFunction) { ULANG_ASSERT(_Scope); if (TSPtr IrContext = GenNodeUnlessModule(AstIdentifierFunction.Context())) { // Hoist the context to avoid duplicating side effects. STempBinding TempBinding = BindValueToTemporaryInNewCodeBlock(Move(IrContext.AsRef())); TSRef TempContext = NewIrNode( _Program, *TempBinding.Definition); TSRef Result = GenInvocation(AstNode, AstIdentifierFunction, Move(TempContext)); TempBinding.CodeBlock->AppendSubExpr(Move(Result)); return Move(TempBinding.CodeBlock); } return GenInvocation(AstNode, AstIdentifierFunction, nullptr); } TSRef GenInvocation(CExprInvocation& AstNode) { const TSPtr& AstCallee = AstNode.GetCallee(); if (AstCallee->GetNodeType() == EAstNodeType::Identifier_Function) { const CExprIdentifierFunction& AstIdentifierFunction = static_cast(*AstCallee); return GenInvocation(AstNode, AstIdentifierFunction); } const CFunctionType& CalleeType = AstCallee->GetResultType(_Program)->GetNormalType().AsChecked(); TSRef IrCallee = GenNode(*AstCallee); TSRef IrArgument = CoerceToType(GenNode(AstNode.GetArgument().AsRef()), &CalleeType.GetParamsType()); TSRef IrNode = NewIrNode( AstNode._CallsiteBracketStyle, Move(IrCallee), Move(IrArgument)); IrNode->SetResolvedCalleeType(AstNode.GetResolvedCalleeType()); IrNode->SetResultType(AstNode.GetResultType(_Program)); return IrNode; } TSRefGenUnaryArithmetic(CExprUnaryArithmetic& AstNode) { return GenInvocation(AstNode); } TSRefGenBinaryArithmetic(CExprBinaryArithmetic& AstNode) { return GenInvocation(AstNode); } TSRefGenShortCircuitAnd(CExprShortCircuitAnd& AstNode) { TSRef IrNode = NewIrNode( GenNode(AstNode.Lhs()), GenNode(AstNode.Rhs())); return IrNode; } TSRefGenShortCircuitOr(CExprShortCircuitOr& AstNode) { const CTypeBase* JoinType = AstNode.GetResultType(_Program); TSRef IrNode = NewIrNode( CoerceToType(GenNode(*AstNode.Lhs()), JoinType), CoerceToType(GenNode(*AstNode.Rhs()), JoinType)); return IrNode; } TSRef GenLogicalNot(CExprLogicalNot& AstNode) { TSRef IrNode = NewIrNode(GenNode(AstNode.Operand())); return IrNode; } TSRef GenComparison(CExprComparison& AstNode) { return GenInvocation(AstNode); } TSRef GenQueryValue(CExprQueryValue& AstNode) { return GenInvocation(AstNode); } TSRef GenMakeOption(CExprMakeOption& AstNode) { const CTypeBase* ValueType = AstNode.GetOptionType(_Program)->GetValueType(); TSRef IrNode = NewIrNode( AstNode.GetResultType(_Program), AstNode.Operand() ? CoerceToType(GenNode(AstNode.Operand().AsRef()), ValueType) : TSPtr()); return IrNode; } TSRef GenMakeArray(CExprMakeArray& AstNode) { const CTypeBase* ElementType = AstNode.GetArrayType(_Program)->GetElementType(); const TSPtrArray& SubExprs = AstNode.GetSubExprs(); TSRef IrNode = NewIrNode(SubExprs.Num()); for (const TSPtr& ElementAst : AstNode.GetSubExprs()) { IrNode->AppendSubExpr(CoerceToType(GenNode(*ElementAst), ElementType)); } return IrNode; } TSRef GenMakeMap(CExprMakeMap& AstNode) { const CMapType* MapType = AstNode.GetMapType(_Program); const CTypeBase* KeyType = MapType->GetKeyType(); const CTypeBase* ValueType = MapType->GetValueType(); const TSPtrArray& SubExprs = AstNode.GetSubExprs(); TSRef IrNode = NewIrNode(SubExprs.Num()); for (const TSPtr& PairAst : AstNode.GetSubExprs()) { ULANG_ASSERTF(PairAst->GetNodeType() == EAstNodeType::Literal_Function, "CExprMakeMap subexpressions must be function literals"); const CExprFunctionLiteral& PairLiteralAst = static_cast(*PairAst); IrNode->AppendSubExpr(NewIrNode( CoerceToType(GenNode(*PairLiteralAst.Domain()), KeyType), CoerceToType(GenNode(*PairLiteralAst.Range()), ValueType))); } return IrNode; } TSRef GenMakeTuple(CExprMakeTuple& AstNode) { const CTupleType* TupleType = AstNode.GetTupleType(_Program); const TSPtrArray& SubExprs = AstNode.GetSubExprs(); TSRef IrNode = NewIrNode(SubExprs.Num()); ULANG_ASSERTF(AstNode.GetSubExprs().Num() == TupleType->GetElements().Num(), "Mismatched number of elements"); for (int32_t ElementIndex = 0; ElementIndex < AstNode.GetSubExprs().Num(); ++ElementIndex) { IrNode->AppendSubExpr(CoerceToType( GenNode(*AstNode.GetSubExprs()[ElementIndex]), (*TupleType)[ElementIndex])); } return IrNode; } TSRef GenTupleElement(CExprTupleElement& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode._TupleExpr), AstNode._ElemIdx, AstNode.GetMappedVstNode()); return IrNode; } TSRef GenMakeRange(CExprMakeRange& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode._Lhs), GenNode(*AstNode._Rhs)); return IrNode; } TSRef GenInvokeType(CExprInvokeType& AstNode) { TSRef IrNode = GenNode(AstNode._Argument); // Elide infallible casts unless they are to void. if (AstNode._bIsFallible) { IrNode = NewIrNode( AstNode._NegativeType, AstNode.GetResultType(_Program), AstNode._bIsFallible, GenNode(AstNode._TypeAst), Move(IrNode)); } return CoerceToType( Move(IrNode), AstNode._NegativeType); } TSRef GenPointerToReference(CExprPointerToReference& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode.Operand())); return IrNode; } TSRef GenSet(CExprSet& AstNode) { return NewIrNode(GenNode(*AstNode.Operand())); } TSRef GenNewPointer(CExprNewPointer& AstNode) { TSRef IrNode = NewIrNode(static_cast(AstNode.GetResultType(_Program)), GenNode(*AstNode._Value)); return IrNode; } TSRef GenReferenceToValue(CExprReferenceToValue& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode.Operand())); return IrNode; } TSRef GenAssignment(CExprAssignment& AstNode) { TSRef IrNode = NewIrNode( AstNode.Op(), GenNode(AstNode.Lhs()), GenNode(AstNode.Rhs())); return IrNode; } TSRef GenArrayTypeFormer(CExprArrayTypeFormer& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode.GetInnerTypeAst())); IrNode->_TypeType = AstNode._TypeType; return IrNode; } TSRef GenGeneratorTypeFormer(CExprGeneratorTypeFormer& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode.GetInnerTypeAst())); IrNode->_TypeType = AstNode._TypeType; return IrNode; } TSRef GenMapTypeFormer(CExprMapTypeFormer& AstNode) { TSRef IrNode = NewIrNode(GenNodes(AstNode.KeyTypeAsts()), GenNode(*AstNode.ValueTypeAst())); IrNode->_TypeType = AstNode._TypeType; return IrNode; } TSRef GenOptionTypeFormer(CExprOptionTypeFormer& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode.GetInnerTypeAst())); IrNode->_TypeType = AstNode._TypeType; return IrNode; } TSRef GenSubtype(CExprSubtype& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode.GetInnerTypeAst())); IrNode->_TypeType = AstNode._TypeType; IrNode->_bCastableSubtype = AstNode._bCastableSubtype; return IrNode; } TSRef GenTupleType(CExprTupleType& AstNode) { const TSPtrArray& ElementTypes = AstNode.GetElementTypeExprs(); TSRef IrNode = NewIrNode(ElementTypes.Num()); for (const TSPtr& ElementType : ElementTypes) { IrNode->GetElementTypeExprs().Add(GenNode(*ElementType)); } IrNode->_TypeType = AstNode._TypeType; return IrNode; } TSRef GenArrow(CExprArrow& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode.Domain()), GenNode(*AstNode.Range())); IrNode->_TypeType = AstNode._TypeType; return IrNode; } TSRef GenArchetypeInstantiation(CExprArchetypeInstantiation& AstNode) { TSRef IrNode = NewIrNode( GenNode(*AstNode._ClassAst), CreateClause(AstNode._BodyAst), AstNode.GetResultType(*_SemanticProgram)); for (const TSRef& Argument : AstNode.Arguments()) { if (Argument->GetNodeType() == EAstNodeType::Definition) { const CExprDefinition& Definition = static_cast(*Argument); const CExprIdentifierData& Element = static_cast(*Definition.Element()); const CDataDefinition& OverriddenPrototypeDefinition = *Element._DataDefinition.GetBaseOverriddenDefinition().GetPrototypeDefinition(); const CTypeBase* PrototypeInitializerType = OverriddenPrototypeDefinition.IsVar() ? OverriddenPrototypeDefinition.GetType()->GetNormalType().AsChecked().PositiveValueType() : OverriddenPrototypeDefinition.GetType(); IrNode->AppendArgument(NewIrNode( NewIrNode(*_SemanticProgram, OverriddenPrototypeDefinition), nullptr, CoerceToType(GenNode(Definition.Value()), PrototypeInitializerType))); } else if (Argument->GetNodeType() == EAstNodeType::Flow_CodeBlock) { IrNode->AppendArgument(GenNode(*Argument)); } else if (Argument->GetNodeType() == EAstNodeType::Flow_Let) { IrNode->AppendArgument(GenNode(*Argument)); } else if (Argument->GetNodeType() == EAstNodeType::Invoke_Invocation) { IrNode->AppendArgument(GenNode(*Argument)); } else { ULANG_ERRORF("Unexpected node type"); } } return IrNode; } TSRef GenCodeBlock(CExprCodeBlock& AstNode) { TGuardValue ScopeGuard(_Scope, AstNode._AssociatedScope.Get()); ULANG_ASSERT(_Scope); const TSPtrArray& SubExprs = AstNode.GetSubExprs(); TSRef IrNode = NewIrNode(SubExprs.Num()); for (const TSPtr& SubExpr : AstNode.GetSubExprs()) { IrNode->AppendSubExpr(GenNode(*SubExpr)); } return IrNode; } TSRef GenLet(CExprLet& AstNode) { const TSPtrArray& SubExprs = AstNode.GetSubExprs(); TSRef IrNode = NewIrNode(SubExprs.Num()); for (const TSPtr& SubExpr : AstNode.GetSubExprs()) { IrNode->AppendSubExpr(GenNode(*SubExpr)); } return IrNode; } TSRef GenDefer(CExprDefer& AstNode) { TSRef IrNode = NewIrNode(); IrNode->SetExpr(GenNode(AstNode.Expr())); return IrNode; } TSRef GenIf(CExprIf& AstNode) { TSRef Condition = GenCodeBlock(*AstNode.GetCondition()); TGuardValue ScopeGuard(_Scope, AstNode.GetCondition()->_AssociatedScope.Get()); ULANG_ASSERT(_Scope); const CTypeBase* ResultType = AstNode.GetResultType(_Program); TSPtr Then = AstNode.GetThenClause() ? CoerceToType(GenNode(*AstNode.GetThenClause()), ResultType) : TSPtr(); TSPtr Else = CoerceToType(GenNode(AstNode.GetElseClause()), ResultType); TSRef IrNode = NewIrNode(Move(Condition), Move(Then), Move(Else)); return IrNode; } bool IsGenerator(CExpressionBase& Expr) { if (Expr.GetNodeType() == EAstNodeType::Definition_Data || Expr.GetNodeType() == EAstNodeType::Definition_IterationPair) { CExprDefinition& Definition = static_cast(Expr); if (Definition.Value()) { const CNormalType& IterableType = Definition.Value()->GetResultType(_Program)->GetNormalType(); return IterableType.IsA(); } if (Definition.ValueDomain()) { const CNormalType& IterableType = Definition.ValueDomain()->GetResultType(_Program)->GetNormalType(); return IterableType.IsA() || IterableType.IsA() || IterableType.IsA(); } } return false; } /* * The CExprIteration type encodes * * for(generators, definitions, conditions) { expr } * * Where the generators, definitions, and conditions can come in any order as long as the first is a generator. * * This is transformed into * * do * { * ir_for(generator) * { * definition * if (condition) * { * resultDestination.add(expr) * } * } * } * * Only one each of generator, definition, and condition is show, but they can be nested arbitrarly as long as * the outermost is a generator. * * ResultDestination is created by the code generator for now. It will be explicit in the IR in the future. */ TSRef GenIrFor(CExprDataDefinition& DataDefinition) { TSRef For = NewIrNode(DataDefinition._DataMember, GenNode(DataDefinition.Element()), GenNode(DataDefinition.ValueDomain()), GenNode(DataDefinition.Value())); return For; } TSRef GenIrFor(CExprIterationPairDefinition& DataDefinition) { TSRef For = NewIrNode(DataDefinition._KeyDefinition, DataDefinition._ValueDefinition, GenNode(DataDefinition.Element()), GenNode(DataDefinition.ValueDomain()), GenNode(DataDefinition.Value())); return For; } TSRef GenIteration(CExprIteration& AstNode) { TGuardValue ScopeGuard(_Scope, AstNode._AssociatedScope.Get()); ULANG_ASSERT(_Scope); CAstPackage* ScopePackage = _Scope->GetPackage(); ULANG_ASSERT(ScopePackage); TSRef IrNode = NewIrNode(2); TSRef CurrentBlock = IrNode; bool bOutermost = true; bool bGenerateResult = true; for (const TSRef& Filter : AstNode._Filters) { TGuardValue MappedVstNodeGuard(_MappedVstNode, Filter->GetMappedVstNode()); if (IsGenerator(*Filter)) { // Generate CIrFor TSRef For = Filter->GetNodeType() == EAstNodeType::Definition_IterationPair ? GenIrFor(static_cast(*Filter)) : GenIrFor(static_cast(*Filter)); For->_bOutermost = bOutermost; For->_bGenerateResult = bGenerateResult; if (bOutermost) { For->_bCanFail = AstNode.CanFail(ScopePackage); } bOutermost = false; bGenerateResult = false; TSRef ForBody = NewIrNode(1); For->SetBody(ForBody); For->IrSetResultType(AstNode.IrGetResultType()); CurrentBlock->AppendSubExpr(Move(For)); CurrentBlock = Move(ForBody); } else { CurrentBlock->AppendSubExpr(GenNode(*Filter)); } } ULANG_ASSERTF(AstNode._Body.IsValid(), "Missing body in for"); CurrentBlock->AppendSubExpr(NewIrNode(NewIrNode(GenNode(*AstNode._Body)))); return IrNode; } TSRef GenLoop(CExprLoop& AstNode) { TSRef IrNode = NewIrNode(); IrNode->SetExpr(GenNode(AstNode.Expr())); return IrNode; } TSRef GenBreak(CExprBreak& AstNode) { TSRef IrNode = NewIrNode(); return IrNode; } TSRef GenReturn(CExprReturn& AstNode) { return NewIrNode( GenNode(AstNode.Result()), AstNode.Function()); } TSRef GenSync(CExprSync& AstNode) { const TSPtrArray& SubExprs = AstNode.GetSubExprs(); TSRef IrNode = NewIrNode(); for (const TSPtr& SubExpr : SubExprs) { IrNode->AppendSubExpr(GenNode(*SubExpr)); } return IrNode; } TSRef GenRush(CExprRush& AstNode) { const CTypeBase* ResultType = AstNode.GetResultType(_Program); const TSPtrArray& SubExprs = AstNode.GetSubExprs(); TSRef IrNode = NewIrNode(); for (const TSPtr& SubExpr : SubExprs) { IrNode->AppendSubExpr(CoerceToType(GenNode(*SubExpr), ResultType)); } return IrNode; } TSRef GenRace(CExprRace& AstNode) { const CTypeBase* ResultType = AstNode.GetResultType(_Program); const TSPtrArray& SubExprs = AstNode.GetSubExprs(); TSRef IrNode = NewIrNode(); for (const TSPtr& SubExpr : SubExprs) { IrNode->AppendSubExpr(CoerceToType(GenNode(*SubExpr), ResultType)); } return IrNode; } TSRef GenSyncIterated(CExprSyncIterated& AstNode) { TSRef IrNode = NewIrNode(); IrNode->SetBody(GenNode(AstNode._Body)); for (const TSRef& Filter : AstNode._Filters) { IrNode->AddFilter(GenNode(*Filter)); } return IrNode; } TSRef GenRushIterated(CExprRushIterated& AstNode) { TSRef IrNode = NewIrNode(); IrNode->SetBody(GenNode(AstNode._Body)); for (const TSRef& Filter : AstNode._Filters) { IrNode->AddFilter(GenNode(*Filter)); } return IrNode; } TSRef GenRaceIterated(CExprRaceIterated& AstNode) { TSRef IrNode = NewIrNode(); IrNode->SetBody(GenNode(AstNode._Body)); for (const TSRef& Filter : AstNode._Filters) { IrNode->AddFilter(GenNode(*Filter)); } return IrNode; } TSRef GenBranch(CExprBranch& AstNode) { TSRef IrNode = NewIrNode(); IrNode->SetExpr(GenNode(AstNode.Expr())); return IrNode; } TSRef GenSpawn(CExprSpawn& AstNode) { TSRef IrNode = NewIrNode(); IrNode->SetExpr(GenNode(AstNode.Expr())); return IrNode; } TSRef GenModuleDefinition(CExprModuleDefinition& AstNode) { TGuardValue ScopeGuard(_Scope, AstNode._SemanticModule); ULANG_ASSERTF(AstNode._SemanticModule->GetAstNode() == &AstNode, "Not this node!"); TSRef IrNode = NewIrNode(AstNode._Name, EVstMappingType::Ir); IrNode->_SemanticModule = AstNode._SemanticModule; IrNode->_SemanticModule->SetIrNode(IrNode.Get()); InitIrMemberDefinitions(*IrNode, AstNode); IrNode->_SemanticModule->SetIrPackage(_PackageCache.Lookup(IrNode->_SemanticModule->GetAstPackage()).Get()); return IrNode; } TSRef GenEnumDefinition(CExprEnumDefinition& AstNode) { ULANG_ASSERTF(AstNode._Enum.GetAstNode() == &AstNode, "Not this node!"); // The BPVM codegen doesn't support enumerator values over byte-size, so flag that as an error if (_TargetVM == SBuildParams::EWhichVM::BPVM) { for (TSRef Member : AstNode._Members) { TSPtr EnumValue = Member.As(); if (EnumValue->_Enumerator->_IntValue < std::numeric_limits::min() || EnumValue->_Enumerator->_IntValue > std::numeric_limits::max()) { AppendGlitch(*EnumValue, EDiagnostic::ErrSemantic_Unsupported, uLang::CUTF8String("Enumerator value `%s` is out of byte-range which is not yet supported", EnumValue->_Enumerator->AsCode().AsCString())); // Avoid spam break; } } } TSRef IrNode = NewIrNode( AstNode._Enum, GenNodes(AstNode._Members), EVstMappingType::Ir); AstNode._Enum.SetIrNode(IrNode); return IrNode; } uLang::CScope* GetModuleScopeForBindings(uLang::CScope* Scope) { for (; Scope; Scope = Scope->GetParentScope()) { if (Scope->GetKind() == uLang::Cases) { return Scope; } } return nullptr; } TSRef GenInterfaceDefinition(CExprInterfaceDefinition& AstNode) { ULANG_ASSERTF(AstNode._Interface.GetAstNode() == &AstNode, "Not this node!"); TSRef IrNode = NewIrNode( AstNode._Interface, EVstMappingType::Ir); TArray> SuperInterfaces = GenNodes(AstNode.SuperInterfaces()); IrNode->SetSuperInterfaces(Move(SuperInterfaces)); TGuardValue ScopeGuard(_Scope, &AstNode._Interface); TArray> Members = GenNodes(AstNode.Members()); IrNode->SetMembers(Move(Members)); AstNode._Interface.SetIrNode(IrNode); CInterface& SemanticInterface = AstNode._Interface; // If data member has value then add a new definition to enclosing definition, this is true even if this is an external unit. // i := interface { V:t = e } // => // V_def:t = e // i := interface { V:t = V_def } // This transformation is only okay for effect-free e, but we already has that restriction due to the use of CDO. uLang::CScope* EnclosingScope = GetModuleScopeForBindings(&SemanticInterface._EnclosingScope); for (uLang::CDataDefinition* DataMember : SemanticInterface.GetDefinitionsOfKind()) { // No override of data members in interfaces, due to semantics if (DataMember->GetOverriddenDefinition() != nullptr) { continue; } // don't generate a property for data members that have getters/setters if (DataMember->_OptionalAccessors) { continue; } // Create definition for init value of property, if any if (DataMember->HasInitializer()) { ULANG_ASSERT(EnclosingScope); uLang::CSymbol NewName = _SemanticProgram->GetSymbols()->AddChecked(uLang::CUTF8String("%s_def", GetQualifiedNameString(*DataMember).AsCString()), true); uLang::TSRef DefaultDataDefinition = EnclosingScope->CreateDataDefinition(NewName, DataMember->GetType()); DefaultDataDefinition->SetHasInitializer(); uLang::CExprDefinition* DataIrNode = DataMember->GetIrNode(); DefaultDataDefinition->SetIrNode(DataIrNode); // Create a default value for the data member in the interface uLang::TSRef DefaultValue = uLang::TSRef::New(*_SemanticProgram, *DefaultDataDefinition); DataMember->DefaultValue = DefaultValue; } } return IrNode; } TSRef GenClassDefinition(CExprClassDefinition& AstNode) { ULANG_ASSERTF(AstNode._Class._Definition->GetAstNode() == &AstNode, "Not this node!"); TSRef IrNode = NewIrNode( AstNode._Class, EVstMappingType::Ir); TArray> SuperTypes = GenNodes(AstNode.SuperTypes()); IrNode->SetSuperTypes(Move(SuperTypes)); TGuardValue ScopeGuard(_Scope, &AstNode._Class); TArray> IrMembers = GenNodes(AstNode.Members()); for (const TSRef& Member : IrMembers) { if (Member->GetNodeType() == EAstNodeType::Flow_CodeBlock) { IrNode->_Class._IrBlockClauses.Add(Member.As().Get()); } } const TArray>& Definitions = AstNode._Class.GetDefinitions(); // Iterate `Definitions` using explicit indices. // `CreateCoercedOverridingFunctionDefinition` may add to `Definitions`, // possibly invalidating iterators. Furthermore, such added functions // do not need to be visited. Computing `NumFunctions` before iterating // ensures this. for (int32_t I = 0, NumDefinitions = Definitions.Num(); I != NumDefinitions; ++I) { if (CDataDefinition* DataDefinition = Definitions[I]->AsNullable()) { ULANG_ASSERT(DataDefinition->GetIrNode()->GetNodeType() == EAstNodeType::Definition_Data); CExprDataDefinition& DefinitionIr = *static_cast(DataDefinition->GetIrNode()); // Data definitions that override an inherited field must coerce the overridden default value to // the overridden field type. if (DefinitionIr.Value().IsValid()) { const CDataDefinition& OverriddenPrototypeDefinition = *DataDefinition->GetBaseOverriddenDefinition().GetPrototypeDefinition(); const CTypeBase* PrototypeInitializerType = OverriddenPrototypeDefinition.IsVar() ? OverriddenPrototypeDefinition.GetType()->GetNormalType().AsChecked().PositiveValueType() : OverriddenPrototypeDefinition.GetType(); DefinitionIr.SetValue(CoerceToType(DefinitionIr.TakeValue().AsRef(), PrototypeInitializerType)); } } else if (CFunction* Function = Definitions[I]->AsNullable()) { if (Function->HasAttributeClass(_Program._nativeClass, _Program)) { // Native methods are required to implement the prototype of // the base overridden definition - i.e., the type-erased // form of the root-most method signature. No coercion // should be generated. continue; } const CFunctionType* Type = Function->_Signature.GetFunctionType(); const CFunctionType& CanonicalType = SemanticTypeUtils::Canonicalize(*Type); TArray CanonicalBaseOverriddenTypes; const CFunction* Next = Function; for (;;) { const CFunction* OverriddenFunction = Next->GetOverriddenDefinition(); // If there is no overridden function, no coercion is // needed. if (!OverriddenFunction) { break; } const CFunction& BaseOverriddenFunction = OverriddenFunction->GetBaseCoercedOverriddenFunction(); const CFunctionType* BaseOverriddenType = BaseOverriddenFunction._Signature.GetFunctionType(); const CFunctionType& CanonicalBaseOverridenType = SemanticTypeUtils::Canonicalize(*BaseOverriddenType); const CFunction* PrototypeBaseOverriddenFunction = BaseOverriddenFunction.GetPrototypeDefinition(); const CFunctionType* PrototypeBaseOverriddenFunctionType = PrototypeBaseOverriddenFunction->_Signature.GetFunctionType(); const CFunctionType& CanonicalPrototypeBaseOverriddenType = SemanticTypeUtils::Canonicalize(*PrototypeBaseOverriddenFunctionType); // If the original function matches the overridden function // for which code will be generated (i.e., the prototype // function), no coercion is needed. if (!NeedsCoercion(CanonicalPrototypeBaseOverriddenType, CanonicalType)) { break; } // Add `CanonicalBaseOverridenType` to // `CanonicalBaseOverriddenTypes` before coercing to each of // `CanonicalBaseOverriddenTypes` (and before coercing to // `CanonicalPrototypeBaseOverriddenType`). An override may // both not match the instantiated base type nor the // prototype base type, and require coercion first to the // instantiated base type, then the prototype base type. CanonicalBaseOverriddenTypes.Add(&CanonicalBaseOverridenType); TSPtr OverridingFunctionDefinition = CreateCoercedOverridingFunctionDefinition( *Function, CanonicalBaseOverriddenTypes, CanonicalPrototypeBaseOverriddenType); // If a coercion is needed, but cannot be created, // `CreateCoercedOverridingFunctionDefinition` produces a // glitch. if (!OverridingFunctionDefinition) { break; } // Mark the coercion as overriding the function for which // its type matches (i.e., the prototype function). OverridingFunctionDefinition->_Function->SetOverriddenDefinition(PrototypeBaseOverriddenFunction); IrMembers.Add(Move(OverridingFunctionDefinition.AsRef())); // If the overridden function matches the prototype // function, all further needed coercions from ancestors // classes are handled when generating coercions for the // prototype function. if (&CanonicalBaseOverridenType == &CanonicalPrototypeBaseOverriddenType) { break; } Next = &BaseOverriddenFunction; } } } IrNode->SetMembers(Move(IrMembers)); AstNode._Class._Definition->SetIrNode(IrNode); return IrNode; } TSRef GenDataDefinition(CExprDataDefinition& AstNode) { TSRef IrNode = NewIrNode( AstNode._DataMember, GenNode(AstNode.Element()), GenNode(AstNode.ValueDomain()), GenNode(AstNode.Value()), EVstMappingType::Ir); AstNode._DataMember->SetIrNode(IrNode); return IrNode; } TSRef GenIterationPairDefinition(CExprIterationPairDefinition& AstNode) { TSRef IrNode = NewIrNode( TSRef(AstNode._KeyDefinition), TSRef(AstNode._ValueDefinition), GenNode(AstNode.Element().Get()), GenNode(AstNode.ValueDomain().Get()), GenNode(AstNode.Value().Get()), EVstMappingType::Ir); return IrNode; } TSRef GenFunctionDefinition(CExprFunctionDefinition& AstNode) { CFunction& Function = *AstNode._Function; TGuardValue ScopeGuard(_Scope, &Function); TGuard FunctionParamDefinitionGuard([this, NumParamDefinitions = _FunctionParamDefinitions.Num()] { _FunctionParamDefinitions.SetNum(NumParamDefinitions); }); for (CDataDefinition* Param : Function._Signature.GetParams()) { _FunctionParamDefinitions.Add(Param); } TSRef IrNode = NewIrNode( AstNode._Function, GenNode(AstNode.Element()), GenNode(AstNode.ValueDomain()), GenNode(AstNode.Value()), EVstMappingType::Ir); AstNode._Function->SetIrNode(IrNode); return IrNode; } TSRef GenTypeAliasDefinition(CExprTypeAliasDefinition& AstNode) { TSRef IrNode = NewIrNode( AstNode._TypeAlias, GenNode(AstNode.Element()), GenNode(AstNode.ValueDomain()), GenNode(AstNode.Value()), EVstMappingType::Ir); return IrNode; } TSRef GenAccessLevelDefinition(CExprScopedAccessLevelDefinition& AstNode) { ULANG_ASSERTF(AstNode._AccessLevelDefinition->GetAstNode() == &AstNode, "Not this node!"); TSRef IrNode = NewIrNode( AstNode._AccessLevelDefinition, EVstMappingType::Ir); IrNode->_ScopeReferenceExprs = GenNodes(AstNode._ScopeReferenceExprs); AstNode._AccessLevelDefinition->SetIrNode(IrNode); return IrNode; } TSRef GenProfileBlock(CExprProfileBlock& AstNode) { TSRef IrNode = NewIrNode(); IrNode->SetExpr(GenNode(AstNode.Expr())); IrNode->_UserTag = GenNode(AstNode._UserTag.Get()); #if WITH_VERSE_BPVM // Cache some tracking structure types for the profiling system IrNode->_ProfileLocusType = GetProgram()->GetProfileLocusType(); IrNode->_ProfileDataType = GetProgram()->GetProfileDataType(); #endif return IrNode; } TSRef GenExprUsing(CExprUsing& AstNode) { TSRef IrNode = NewIrNode(GenNode(*AstNode._Context)); IrNode->_Module = AstNode._Module; return IrNode; } TSRef GenExprImport(CExprImport& AstNode) { TSRef IrNode = NewIrNode(AstNode._ModuleAlias, GenNode(*AstNode._Path), EVstMappingType::Ir); return IrNode; } TSRef GenExprWhere(CExprWhere& AstNode) { TSRef IrLhs = GenNode(*AstNode.Lhs()); const TSPtrArray& RhsArray = AstNode.Rhs(); TSPtrArray IrRhs; IrRhs.Reserve(RhsArray.Num()); for (const TSPtr& Rhs : RhsArray) { IrRhs.Add(GenNode(*Rhs)); } return NewIrNode( Move(IrLhs), Move(IrRhs)); } TSRef GenVar(CExprVar& AstNode) { return NewIrNode(GenNode(*AstNode.Operand())); } TSRef GenMakeNamed(CExprMakeNamed& AstNode) { return NewIrNode( AstNode.GetName(), GenNode(*AstNode.GetNameIdentifier()), GenNode(*AstNode.GetValue())); } TSRef GenExprSnippet(CExprSnippet& AstNode) { TSRef IrNode = NewIrNode(AstNode._Path); IrNode->_SemanticSnippet = AstNode._SemanticSnippet; InitIrMemberDefinitions(*IrNode, AstNode); return IrNode; } template void AppendGlitch(const CAstNode& AstNode, ResultArgsType&&... ResultArgs) { SGlitchResult Glitch(uLang::ForwardArg(ResultArgs)...); ULANG_ASSERTF( AstNode.GetMappedVstNode() && AstNode.GetMappedVstNode()->Whence().IsValid(), "Expected valid whence for node used as glitch locus on %s id:%i - %s", AstNode.GetErrorDesc().AsCString(), GetDiagnosticInfo(Glitch._Id).ReferenceCode, Glitch._Message.AsCString()); _Diagnostics.AppendGlitch(Move(Glitch), SGlitchLocus(&AstNode)); } template TSRef NewIrNode(Parameters&&... Args) { TSRef IrNode = TSRef::New(uLang::ForwardArg(Args)...); IrNode->SetIrMappedVstNode(_MappedVstNode); return Move(IrNode); } // When no matching coerced function is found, write the number of existing coerced versions of `Function` to `OutNumCoerced`. const CFunction* FindCoercedFunction(const CFunction& Function, const CFunctionType& CoercedType, std::size_t& OutNumCoerced) { std::size_t NumCoerced = 0; for (auto&& [FunctionPtr, CoercedTypePtr, CoercedFunctionPtr] : _CoercedFunctions) { if (FunctionPtr == &Function) { NumCoerced++; if (CoercedTypePtr == &CoercedType) { return CoercedFunctionPtr; } } } OutNumCoerced = NumCoerced; return nullptr; } CDataDefinition* FindFunctionParamDefinition(const CExprDefinition& AstNode) { auto Last = _FunctionParamDefinitions.end(); auto I = uLang::FindIf(_FunctionParamDefinitions.begin(), Last, [&](const CDataDefinition* Arg) { return Arg->GetAstNode() == &AstNode; }); return I == Last? nullptr : *I; } const TSRef _SemanticProgram; CSemanticProgram& _Program; CDiagnostics& _Diagnostics; struct SCoercedFunctionDefinition { const CFunction* Function; const CFunctionType* CoercedType; const CFunction* CoercedFunction; }; TArray _CoercedFunctions; TArray _FunctionParamDefinitions; CScope* _Scope{nullptr}; SBuildParams::EWhichVM _TargetVM; const Vst::Node* _MappedVstNode{nullptr}; }; //==================================================================================== // CIrGenerate Implementation //==================================================================================== //------------------------------------------------------------------------------------------------- bool GenerateIr(const TSRef& Program, const TSRef& Diagnostics, SBuildParams::EWhichVM TargetVM) { TURef IrGenerator = TURef::New(Program, Diagnostics, TargetVM); return IrGenerator->ProcessAst(); } }