Files
UnrealEngine/Engine/Source/Runtime/VerseCompiler/Public/uLang/Semantics/SemanticScope.h
2025-05-18 13:04:45 +08:00

337 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
// uLang Compiler Public API
#pragma once
#include "uLang/Common/Containers/Array.h"
#include "uLang/Common/Containers/SharedPointer.h"
#include "uLang/Common/Containers/Function.h"
#include "uLang/Common/Text/UTF8String.h"
#include "uLang/Semantics/FilteredDefinitionRange.h"
#include "uLang/Semantics/MemberOrigin.h"
#include "uLang/Semantics/Revision.h"
#include "uLang/Semantics/SmallDefinitionArray.h"
#include "uLang/Semantics/StructOrClass.h"
#include "uLang/Semantics/VisitStamp.h"
#define UE_API VERSECOMPILER_API
namespace uLang
{
struct SAccessLevel;
struct SQualifier;
class CAstPackage;
class CAstCompilationUnit;
class CControlScope;
class CClass;
class CClassDefinition;
class CDataDefinition;
class CEnumeration;
class CEnumerator;
class CFunction;
class CInterface;
class CModule;
class CModulePart;
class CModuleAlias;
class CSnippet;
class CLogicalScope;
class CSemanticAnalyzerImpl;
class CSemanticProgram;
class CScopedAccessLevelDefinition;
class CTypeBase;
class CTypeAlias;
class CTypeScope;
class CTypeType;
class CTypeVariable;
/**
* Stores a resolved definition and the context that it was resolved from
*/
struct SResolvedDefinition
{
CDefinition* _Definition;
const CDataDefinition* _Context;
SResolvedDefinition(CDefinition* Definition) : _Definition(Definition), _Context(nullptr) {}
SResolvedDefinition(CDefinition* Definition, const CDataDefinition* Context) : _Definition(Definition), _Context(Context) {}
};
/**
* An array of resolved definitions and their associated contexts
*/
using SResolvedDefinitionArray = TArrayG<SResolvedDefinition, TInlineElementAllocator<1>>;
enum class EPathMode : uint8_t { Default, PrefixSeparator, PackageRelative, PackageRelativeWithRoot };
/**
* A nested scope - program, module or class
*/
class CScope
{
public:
enum class EKind : uint8_t
{
Program,
CompatConstraintRoot,
Module,
ModulePart,
Snippet,
Class,
Function,
ControlScope, // A nested scope within a function body
Interface,
Type,
Enumeration
};
static UE_API const char* KindToCString(EKind Kind);
CScope(EKind Kind, CScope* Parent, CSemanticProgram& Program) : _Kind(Kind), _Parent(Parent), _Program(Program) {}
UE_API virtual ~CScope();
// Delete the generated move/copy constructors so that they don't require full definitions of the types referenced by TSRefArray.
CScope(const CScope&) = delete;
CScope(CScope&&) = delete;
virtual CSymbol GetScopeName() const = 0;
virtual const CTypeBase* ScopeAsType() const { return nullptr; }
virtual const CDefinition* ScopeAsDefinition() const { return nullptr; }
virtual SAccessLevel GetDefaultDefinitionAccessLevel() const { return SAccessLevel::EKind::Internal; }
ULANG_FORCEINLINE EKind GetKind() const { return _Kind; }
ULANG_FORCEINLINE CScope* GetParentScope() const { return _Parent; }
UE_API const CScope* GetScopeOfKind(EKind Kind) const;
using EPathMode = uLang::EPathMode;
UE_API CUTF8String GetScopePath(uLang::UTF8Char SeparatorChar = '.', EPathMode Mode = EPathMode::Default) const;
UE_API const CModule* GetModule() const;
UE_API CModule* GetModule();
UE_API const CModulePart* GetModulePart() const;
UE_API CModulePart* GetModulePart();
UE_API CAstPackage* GetPackage() const;
UE_API CAstCompilationUnit* GetCompilationUnit() const;
UE_API const CSnippet* GetSnippet() const;
UE_API const TSPtr<CSymbolTable>& GetSymbols() const;
ULANG_FORCEINLINE CSemanticProgram& GetProgram() const { return _Program; }
/// If this is a parametric type, get the scope of those parameters; otherwise returns this scope.
UE_API const CScope& GetParametricTypeScope() const;
/// Get the innermost logical scope that is or contains this scope.
UE_API const CLogicalScope& GetLogicalScope() const;
ULANG_FORCEINLINE CLogicalScope& GetLogicalScope() { return const_cast<CLogicalScope&>(static_cast<const CScope*>(this)->GetLogicalScope()); }
/// Iff this scope is a logical scope, return it a pointer to it. Otherwise, return null.
virtual const CLogicalScope* AsLogicalScopeNullable() const { return nullptr; }
virtual CLogicalScope* AsLogicalScopeNullable() { return nullptr; }
ULANG_FORCEINLINE bool IsLogicalScope() const { return AsLogicalScopeNullable() != nullptr; }
const CLogicalScope* GetEnclosingClassOrInterface() const { return const_cast<CScope*>(this)->GetEnclosingClassOrInterface(); }
UE_API CLogicalScope* GetEnclosingClassOrInterface();
// Check if this module is the same or a child of another
UE_API bool IsSameOrChildOf(const CScope* Other) const;
// Determines if this is either a function body or a nested scope within a function body
bool IsControlScope() const { return _Kind == EKind::ControlScope || _Kind == EKind::Function; }
// Determines if inside a type scope, ignoring control scope
UE_API bool IsInsideTypeScope() const;
// Determines if this is a module or snippet scope.
bool IsModuleOrSnippet() const { return GetKind() == EKind::Module || GetKind() == EKind::ModulePart || GetKind() == EKind::Snippet; }
// Determines if the definitions in this scope are built-in.
UE_API bool IsBuiltInScope() const;
UE_API CModule& CreateModule(const CSymbol& ModuleName);
UE_API CClassDefinition& CreateClass(const CSymbol& ClassName, CClass* Superclass = nullptr, TArray<CInterface*>&& SuperInterfaces = {}, EStructOrClass StructOrClass = EStructOrClass::Class);
UE_API CEnumeration& CreateEnumeration(const CSymbol& EnumerationName);
UE_API CInterface& CreateInterface(const CSymbol& InterfaceName, const TArray<CInterface*>& SuperInterfaces = {});
UE_API TSRef<CFunction> CreateFunction(const CSymbol FunctionName);
virtual void CreateNegativeFunction(const CFunction& PositiveFunction) const {}
UE_API TSRef<CDataDefinition> CreateDataDefinition(const CSymbol VarName);
UE_API TSRef<CDataDefinition> CreateDataDefinition(const CSymbol VarName, const CTypeBase* Type);
virtual void CreateNegativeDataDefinition(const CDataDefinition& PositiveDataDefinition) const {}
UE_API TSRef<CTypeAlias> CreateTypeAlias(const CSymbol Name);
UE_API TSRef<CTypeVariable> CreateTypeVariable(const CSymbol Name, const CTypeBase* NegativeType, const CTypeBase* PositiveType);
UE_API TSRef<CModuleAlias> CreateModuleAlias(const CSymbol Name);
UE_API TSRef<CScopedAccessLevelDefinition> CreateScopedAccessLevelDefinition(TOptional<CSymbol> ClassName);
// Using declarations
void AddUsingScope(const CLogicalScope* UsingScope) { _UsingScopes.AddUnique(UsingScope); }
const TArray<const CLogicalScope*>& GetUsingScopes() const { return _UsingScopes; }
// Add a local context to infer from a using declaration - return nullptr if added and conflicting context if type/value domain was already previously added
UE_API const CDataDefinition* AddUsingInstance(const CDataDefinition* UsingContext);
const TArray<const CDataDefinition*>& GetUsingInstances() const { return _UsingInstances; }
static UE_API void ResolvedDefnsAppend(SResolvedDefinitionArray* ResolvedDefns, const SmallDefinitionArray& Definitions);
static UE_API void ResolvedDefnsAppendWithContext(SResolvedDefinitionArray* ResolvedDefns, const SmallDefinitionArray& Definitions, const CDataDefinition* Context);
/// Look for a definition in this scope and all parent scopes and aliases
UE_API SResolvedDefinitionArray ResolveDefinition(const CSymbol& Name, const SQualifier& Qualifier = SQualifier::Unknown(), const CAstPackage* ContextPackage = nullptr) const;
UE_API TSRef<CControlScope> CreateNestedControlScope(CSymbol Name = CSymbol());
const TSRefArray<CControlScope>& GetNestedControlScopes() const { return _NestedControlScopes; }
UE_API TSRef<CTypeScope> CreateNestedTypeScope();
/// Generates a new stamp id
static UE_API VisitStampType GenerateNewVisitStamp();
// Determines whether this scope was authored by Epic
UE_API bool IsAuthoredByEpic() const;
// Determines whether this scope can access Epic-internal definitions.
// This differs from IsAuthoredByEpic by allowing packages with Scope=InternalUser to access epic-internal definitions.
UE_API bool CanAccessEpicInternal() const;
protected:
friend class CSemanticAnalyzerImpl;
friend class CDefinition;
friend class CDataDefinition;
// Returns whether some definition is accessible from this scope.
// When checking accessibility, you probably want to use CDefinition::IsAccessibleFrom
// instead of this.
UE_API bool CanAccess(const CDefinition& Definition, const SAccessLevel& DefinitionAccessLevel) const;
// If we are a program, module etc.
EKind _Kind;
// The enclosing scope for this scope
CScope* _Parent;
// The semantic program these types belongs to
CSemanticProgram& _Program;
// `using` declarations referring to other scopes / modules
TArray<const CLogicalScope*> _UsingScopes;
// `using` declarations referring to implied contexts / receivers
TArray<const CDataDefinition*> _UsingInstances;
// Nested control scopes
TSRefArray<CControlScope> _NestedControlScopes;
// Nested type scopes
TSRefArray<CTypeScope> _NestedTypeScopes;
};
/**
* A scope that can contain definitions
*/
class CLogicalScope : public CScope
{
public:
CLogicalScope(EKind Kind, CScope* Parent, CSemanticProgram& Program) : CScope(Kind, Parent, Program), _LastVisitStamp(0) {}
UE_API virtual ~CLogicalScope();
// Delete the generated move/copy constructors so that they don't require full definitions of the types referenced by TSRefArray.
CLogicalScope(const CLogicalScope&) = delete;
CLogicalScope(CLogicalScope&&) = delete;
/// Iterates through all the logical scopes nested inside this scope
UE_API EIterateResult IterateRecurseLogicalScopes(const TFunction<EVisitResult(const CLogicalScope&)>& Functor) const;
UE_API EIterateResult IterateRecurseLogicalScopes(TFunction<EVisitResult(const CLogicalScope&)>&& Functor) const;
const TArray<TSRef<CDefinition>>& GetDefinitions() const { return _Definitions; }
template<typename FilterClass>
TFilteredDefinitionRange<FilterClass> GetDefinitionsOfKind() const;
UE_API virtual SmallDefinitionArray FindDefinitions(
const CSymbol& Name,
EMemberOrigin Origin = EMemberOrigin::InheritedOrOriginal,
const SQualifier& Qualifier = SQualifier::Unknown(),
const CAstPackage* ContextPackage = nullptr,
VisitStampType VisitStamp = GenerateNewVisitStamp()) const;
template<typename FilterClass>
FilterClass* FindFirstDefinitionOfKind(
const CSymbol& Name,
EMemberOrigin Origin = EMemberOrigin::InheritedOrOriginal,
const SQualifier& Qualifier = SQualifier::Unknown(),
const CAstPackage* ContextPackage = nullptr,
VisitStampType VisitStamp = GenerateNewVisitStamp()) const;
UE_API virtual void SetRevision(SemanticRevision Revision);
SemanticRevision GetRevision() const { return _CumulativeRevision; }
// If this scope has the given visit stamp, return false.
// Otherwise, mark this scope with the visit stamp and return true.
// Use CScope::GenerateNewVisitStamp to get a new visit stamp.
ULANG_FORCEINLINE bool TryMarkVisited(VisitStampType VisitStamp) const
{
ULANG_ASSERTF(VisitStamp >= _LastVisitStamp, "Guard against situations where this is used in a nested context.");
if (_LastVisitStamp == VisitStamp)
{
return false;
}
else
{
_LastVisitStamp = VisitStamp;
return true;
}
}
// Allocates an ordinal for the next definition in this scope.
int32_t AllocateNextDefinitionOrdinal()
{
return _NextDefinitionOrdinal++;
}
/// Get the matching override definition in this class for the argument, if there is any
UE_API const CDefinition* FindOverrideFor(const CDefinition& Definition) const;
// CScope interface.
virtual const CLogicalScope* AsLogicalScopeNullable() const override { return this; }
virtual CLogicalScope* AsLogicalScopeNullable() override { return this; }
UE_API SQualifier AsQualifier() const;
protected:
friend class CScope;
// All definitions in this scope.
TArray<TSRef<CDefinition>> _Definitions;
/// When anything in this class (methods, data members etc.) or its subclasses was last modified/deleted
SemanticRevision _CumulativeRevision = 1; // Initialize semantic revision to 1 to trigger full rebuild on first compile
// To make sure we don't visit the same scope twice during an iteration
mutable VisitStampType _LastVisitStamp { 0 };
private:
// The next ordinal to assign to definitions within this scope.
int32_t _NextDefinitionOrdinal{ 0 };
};
template<typename FilterClass>
TFilteredDefinitionRange<FilterClass> CLogicalScope::GetDefinitionsOfKind() const
{
return TFilteredDefinitionRange<FilterClass>(_Definitions.begin(), _Definitions.end());
}
template<typename FilterClass>
FilterClass* CLogicalScope::FindFirstDefinitionOfKind(const CSymbol& Name, EMemberOrigin Origin, const SQualifier& Qualifier, const CAstPackage* ContextPackage, VisitStampType VisitStamp) const
{
SmallDefinitionArray Definitions = FindDefinitions(Name, Origin, Qualifier, ContextPackage, VisitStamp);
for (CDefinition* Definition : Definitions)
{
if (FilterClass* Result = Definition->AsNullable<FilterClass>())
{
return Result;
}
}
return nullptr;
}
};
#undef UE_API