Files
UnrealEngine/Engine/Source/Runtime/VerseCompiler/Private/uLang/Semantics/SemanticClass.cpp
2025-05-18 13:04:45 +08:00

572 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "uLang/Semantics/SemanticClass.h"
#include "uLang/Common/Algo/FindIf.h"
#include "uLang/Semantics/MemberOrigin.h"
#include "uLang/Semantics/SemanticInterface.h"
#include "uLang/Semantics/SemanticProgram.h"
#include "uLang/Semantics/SmallDefinitionArray.h"
#include "uLang/Semantics/TypeVariable.h"
#include "uLang/Semantics/VisitStamp.h"
#include "uLang/SourceProject/VerseVersion.h"
#include "uLang/Semantics/Expression.h"
namespace uLang
{
CClass::CClass(
CClassDefinition* Definition,
CScope& EnclosingScope,
CClass* Superclass /*= nullptr*/,
TArray<CInterface*>&& SuperInterfaces /*= {}*/,
EStructOrClass StructOrClass /*= EStructOrClass::Class*/,
SEffectSet ConstructorEffects /*= EffectSets::ClassDefault*/)
: CClass(&EnclosingScope, Definition, StructOrClass, Superclass, Move(SuperInterfaces), ConstructorEffects, this, {})
{
}
CClass::CClass(
CScope* ParentScope,
CClassDefinition* Definition,
EStructOrClass StructOrClass,
CClass* Superclass,
TArray<CInterface*>&& SuperInterfaces,
SEffectSet ConstructorEffects,
CClass* GeneralizedClass,
TArray<STypeVariableSubstitution> TypeVariableSubstitutions)
: CNominalType(StaticTypeKind, ParentScope->GetProgram())
, CLogicalScope(CScope::EKind::Class, ParentScope, ParentScope->GetProgram())
, _Definition(Definition)
, _StructOrClass(StructOrClass)
, _Superclass(Superclass)
, _SuperInterfaces(Move(SuperInterfaces))
, _ConstructorEffects(ConstructorEffects)
, _GeneralizedClass(GeneralizedClass)
, _TypeVariableSubstitutions(Move(TypeVariableSubstitutions))
, _OwnedNegativeClass(TUPtr<CClass>::New(this))
, _NegativeClass(_OwnedNegativeClass.Get())
{
_bHasCyclesBroken = Definition->_bHasCyclesBroken;
}
CClass::CClass(CClass* PositiveClass)
: CNominalType(StaticTypeKind, PositiveClass->GetProgram())
, CLogicalScope(CScope::EKind::Class, PositiveClass->GetParentScope(), PositiveClass->GetProgram())
, _Definition(PositiveClass->_Definition)
, _StructOrClass(PositiveClass->_StructOrClass)
, _Superclass(PositiveClass->_Superclass? PositiveClass->_Superclass->_NegativeClass : nullptr)
, _SuperInterfaces(GetNegativeInterfaces(PositiveClass->_SuperInterfaces))
, _ConstructorEffects(PositiveClass->_ConstructorEffects)
, _GeneralizedClass(PositiveClass->_GeneralizedClass)
, _NegativeClass(PositiveClass)
{
}
const CTypeType* CClass::GetTypeType() const
{
return &GetProgram().GetOrCreateTypeType(this->_NegativeClass, this);
}
bool CClass::IsAbstract() const
{
return _Definition->_EffectAttributable.HasAttributeClass(GetProgram()._abstractClass, GetProgram());
}
bool CClass::IsPersistent() const
{
return _Definition->_EffectAttributable.HasAttributeClass(GetProgram()._persistentClass, GetProgram());
}
// Only callable Phase > Deferred_Attributes
bool CClass::IsUnique() const
{
if (_Definition->_EffectAttributable.HasAttributeClass(GetProgram()._uniqueClass, GetProgram()))
{
return true;
}
// The <unique> effect is heritable
if (_Superclass && _Superclass->IsUnique())
{
return true;
}
for (const CInterface* Interface : _SuperInterfaces)
{
if (Interface->IsUnique())
{
return true;
}
}
return false;
}
bool CClass::HasConcreteAttribute() const
{
return _Definition->_EffectAttributable.HasAttributeClass(GetProgram()._concreteClass, GetProgram());
}
const CClass* CClass::FindConcreteBase() const
{
for (const CClass* Class = this; Class != nullptr; Class = Class->_Superclass)
{
if (Class->HasConcreteAttribute())
{
return Class;
}
}
return nullptr;
}
const CClass* CClass::FindInitialConcreteBase() const
{
const CClass* Result = nullptr;
for (const CClass* Class = this; Class != nullptr; Class = Class->_Superclass)
{
if (Class->HasConcreteAttribute())
{
Result = Class;
}
}
return Result;
}
bool CClass::HasCastableAttribute() const
{
return _Definition->_EffectAttributable.HasAttributeClass(GetProgram()._castableClass, GetProgram());
}
const CNominalType* CClass::FindExplicitlyCastableBase() const
{
for (const CClass* Class = this; Class != nullptr; Class = Class->_Superclass)
{
if (Class->HasCastableAttribute())
{
return Class;
}
for (const CInterface* Interface : Class->_SuperInterfaces)
{
if (const CNominalType* Result = Interface->FindExplicitlyCastableBase())
{
return Result;
}
}
}
return nullptr;
}
bool CClass::HasFinalSuperBaseAttribute() const
{
return _Definition->_EffectAttributable.HasAttributeClass(GetProgram()._finalSuperBaseClass, GetProgram());
}
bool CClass::HasFinalSuperAttribute() const
{
return _Definition->_EffectAttributable.HasAttributeClass(GetProgram()._finalSuperClass, GetProgram());
}
SAccessLevel CClass::GetDefaultDefinitionAccessLevel() const
{
const CAstPackage* Package = GetPackage();
return IsStruct() && (!Package || Package->_EffectiveVerseVersion >= Verse::Version::StructFieldsMustBePublic)
? SAccessLevel::EKind::Public
: CLogicalScope::GetDefaultDefinitionAccessLevel();
}
void CClass::CreateNegativeDataDefinition(const CDataDefinition& PositiveDataDefinition) const
{
TSRef<CDataDefinition> NegativeDataDefinition = _NegativeClass->CreateDataDefinition(PositiveDataDefinition.GetName());
NegativeDataDefinition->SetPrototypeDefinition(*PositiveDataDefinition.GetPrototypeDefinition());
}
void CClass::CreateNegativeFunction(const CFunction& PositiveFunction) const
{
CreateNegativeMemberFunction(*_NegativeClass, PositiveFunction);
}
CUTF8String CClass::AsCodeRecursive(ETypeSyntaxPrecedence OuterPrecedence, TArray<const CFlowType*>& VisitedFlowTypes, bool bLinkable, ETypeStringFlag Flag) const
{
if (GetParentScope()->GetKind() != CScope::EKind::Function)
{
return CNominalType::AsCodeRecursive(OuterPrecedence, VisitedFlowTypes, bLinkable, Flag);
}
CUTF8StringBuilder Builder;
if (Flag == ETypeStringFlag::Qualified)
{
CUTF8String Name = GetQualifiedNameString(*GetParentScope()->ScopeAsDefinition());
Builder.Append(Name.AsCString());
}
else
{
CSymbol Name = GetParentScope()->GetScopeName();
Builder.Append(Name.AsStringView());
}
Builder.Append('(');
const TArray<STypeVariableSubstitution>* InstTypeVariables;
if (_OwnedNegativeClass)
{
InstTypeVariables = &_TypeVariableSubstitutions;
}
else
{
InstTypeVariables = &_NegativeClass->_TypeVariableSubstitutions;
}
const char* Separator = "";
for (const STypeVariableSubstitution& InstTypeVariable : *InstTypeVariables)
{
if (!InstTypeVariable._TypeVariable->_ExplicitParam || !InstTypeVariable._TypeVariable->_NegativeTypeVariable)
{
continue;
}
Builder.Append(Separator);
Separator = ",";
const CTypeBase* Type;
if (_OwnedNegativeClass)
{
Type = InstTypeVariable._PositiveType;
}
else
{
Type = InstTypeVariable._NegativeType;
}
Builder.Append(Type->AsCodeRecursive(ETypeSyntaxPrecedence::List, VisitedFlowTypes, bLinkable, Flag));
}
Builder.Append(')');
return Builder.MoveToString();
}
SmallDefinitionArray CClass::FindDefinitions(const CSymbol& Name, EMemberOrigin Origin, const SQualifier& Qualifier, const CAstPackage* ContextPackage, VisitStampType VisitStamp) const
{
SmallDefinitionArray Result = CLogicalScope::FindDefinitions(Name, Origin, Qualifier, ContextPackage, VisitStamp);
if (Origin != EMemberOrigin::Original)
{
Result.Append(FindInstanceMember(Name, EMemberOrigin::Inherited, Qualifier, ContextPackage, VisitStamp));
}
return Result;
}
SmallDefinitionArray CClass::FindInstanceMember(const CSymbol& Name, EMemberOrigin Origin, const SQualifier& Qualifier, const CAstPackage* ContextPackage, VisitStampType VisitStamp) const
{
SmallDefinitionArray Result;
// Diamond inheritance (of interfaces) makes it neccessary to
// first do class inheritance ...
const CClass* OriginClass = Origin == EMemberOrigin::Inherited ? this->_Superclass : this;
while (OriginClass && OriginClass->TryMarkVisited(VisitStamp))
{
// FindDefinition will filter on Qualifier
Result.Append(OriginClass->FindDefinitions(Name, EMemberOrigin::Original, Qualifier, ContextPackage, VisitStamp));
OriginClass = Origin == EMemberOrigin::Original ? nullptr : OriginClass->_Superclass;
}
// .. and then all interfaces.
if (Origin != EMemberOrigin::Original)
{
VisitStampType InterfaceVisitStamp = GenerateNewVisitStamp();
OriginClass = this;
while (OriginClass && OriginClass->TryMarkVisited(InterfaceVisitStamp))
{
for (CInterface* SuperInterface : OriginClass->_SuperInterfaces)
{
Result.Append(SuperInterface->FindInstanceMember(Name, EMemberOrigin::InheritedOrOriginal, Qualifier, ContextPackage, VisitStamp));
}
OriginClass = OriginClass->_Superclass;
}
}
return Result;
}
EComparability CClass::GetComparability() const
{
return GetComparability(GenerateNewVisitStamp());
}
EComparability CClass::GetComparability(VisitStampType VisitStamp) const
{
const CSemanticProgram& Program = GetProgram();
if (IsStruct())
{
// Structs are only comparable following the versioned change to require struct fields to be public.
if (GetPackage()->_EffectiveVerseVersion < Verse::Version::StructFieldsMustBePublic)
{
return EComparability::Incomparable;
}
// Structs with the @import_as attribute are treated as though they have some incomparable field.
if (Definition()->HasAttributeFunctionHack(Program._import_as.Get(), Program))
{
return EComparability::Incomparable;
}
// Otherwise, if a struct has only comparable fields, it is considered to be comparable.
bool bAllDataMembersAreHashable = true;
for (const CDataDefinition* DataMember : GetDefinitionsOfKind<CDataDefinition>())
{
const EComparability DataMemberComparability = DataMember->GetType() ? DataMember->GetType()->GetNormalType().GetComparability() : EComparability::Incomparable;
switch (DataMemberComparability)
{
case EComparability::Comparable:
bAllDataMembersAreHashable = false;
break;
case EComparability::ComparableAndHashable:
break;
case EComparability::Incomparable:
return EComparability::Incomparable;
default:
ULANG_UNREACHABLE();
}
}
// The struct is only hashable if all data members are hashable.
return bAllDataMembersAreHashable ? EComparability::ComparableAndHashable : EComparability::Comparable;
}
// The class is only `comparable` if it has the `unique` attribute in its class inheritance chain
// or its interface inheritance chain.
for (const CClass* DefinitionClass = this; DefinitionClass; DefinitionClass = DefinitionClass->_Superclass)
{
if (!DefinitionClass->TryMarkVisited(VisitStamp))
{
break;
}
// Should perhaps use DefinitionClass->IsUnique(), but that isn't resolved
// until the semantic analyzer is past the Deferred_Attributes phase
if (DefinitionClass->_Definition->_EffectAttributable.HasAttributeClassHack(Program._uniqueClass, Program))
{
return EComparability::ComparableAndHashable;
}
for (CInterface* Interface : DefinitionClass->_SuperInterfaces)
{
if (Interface->GetComparability(VisitStamp) == EComparability::ComparableAndHashable)
{
return EComparability::ComparableAndHashable;
}
}
}
return EComparability::Incomparable;
}
bool CClass::IsPersistable() const
{
return _Definition->_EffectAttributable.HasAttributeClassHack(GetProgram()._persistableClass, GetProgram());
}
bool CClass::ImplementsInterface(const CInterface& Interface) const
{
for (const CInterface* SuperInterface : _SuperInterfaces)
{
if (SuperInterface->IsInterface(Interface))
{
return true;
}
}
if (_Superclass)
{
return _Superclass->ImplementsInterface(Interface);
}
return false;
}
void CClassDefinition::SetAstNode(CExprClassDefinition* AstNode)
{
CDefinition::SetAstNode(AstNode);
}
CExprClassDefinition* CClassDefinition::GetAstNode() const
{
return static_cast<CExprClassDefinition*>(CDefinition::GetAstNode());
}
TArray<TSRef<CExpressionBase>> CClassDefinition::FindMembersWithPredictsAttribute() const
{
TArray<TSRef<CExpressionBase>> Result;
for (const TSRef<CExpressionBase>& Member : GetAstNode()->Members())
{
if (const auto& SourceFieldAst = uLang::AsNullable<CExprDataDefinition>(Member);
SourceFieldAst && SourceFieldAst->_DataMember->GetPrototypeDefinition()->HasPredictsAttribute())
{
Result.Add(Member);
}
else if (const auto& SourceFuncAst = uLang::AsNullable<CExprFunctionDefinition>(Member);
SourceFuncAst && SourceFuncAst->HasUserAddedPredictsEffect(GetProgram()))
{
Result.Add(Member);
}
}
return Result;
}
void CClassDefinition::SetIrNode(CExprClassDefinition* AstNode)
{
CDefinition::SetIrNode(AstNode);
}
CExprClassDefinition* CClassDefinition::GetIrNode(bool bForce) const
{
return static_cast<CExprClassDefinition*>(CDefinition::GetIrNode(bForce));
}
const CNormalType& CInstantiatedClass::CreateNormalType() const
{
if (CClass* InstClass = InstantiateClass(*_Class, GetPolarity(), GetSubstitutions()))
{
return *InstClass;
}
return *_Class;
}
static CClass* FindInstantiatedClass(const TURefArray<CClass>& InstClasses, const TArray<STypeVariableSubstitution>& InstTypeVariables)
{
auto Last = InstClasses.end();
auto I = uLang::FindIf(InstClasses.begin(), Last, [&](CClass* InstClass)
{
return InstClass->_TypeVariableSubstitutions == InstTypeVariables;
});
if (I == Last)
{
return nullptr;
}
return *I;
}
static void CreateNegativeClassMemberDefinitions(const CClass& PositiveClass)
{
CClass* NegativeClass = PositiveClass._NegativeClass;
NegativeClass->_GeneralizedClass = PositiveClass._GeneralizedClass;
for (const CDefinition* Definition : PositiveClass.GetDefinitions())
{
if (const CDataDefinition* PositiveDataDefinition = Definition->AsNullable<CDataDefinition>())
{
PositiveClass.CreateNegativeDataDefinition(*PositiveDataDefinition);
}
else if (const CFunction* PositiveFunction = Definition->AsNullable<CFunction>())
{
PositiveClass.CreateNegativeFunction(*PositiveFunction);
}
}
}
static void InstantiatePositiveDataDefinition(
CLogicalScope& InstScope,
const CNormalType& InstType,
const CDataDefinition& DataDefinition,
const TArray<STypeVariableSubstitution>& Substitutions)
{
TSRef<CDataDefinition> InstDataMember = InstScope.CreateDataDefinition(DataDefinition.GetName());
InstDataMember->SetPrototypeDefinition(*DataDefinition.GetPrototypeDefinition());
SetInstantiatedOverriddenDefinition(*InstDataMember, InstType, DataDefinition);
const CTypeBase* NegativeDataMemberType = DataDefinition._NegativeType;
const CTypeBase* PositiveDataMemberType = DataDefinition.GetType();
NegativeDataMemberType = SemanticTypeUtils::Substitute(*NegativeDataMemberType, ETypePolarity::Negative, Substitutions);
PositiveDataMemberType = SemanticTypeUtils::Substitute(*PositiveDataMemberType, ETypePolarity::Positive, Substitutions);
InstDataMember->_NegativeType = NegativeDataMemberType;
InstDataMember->SetType(PositiveDataMemberType);
}
CClass* InstantiatePositiveClass(const CClass& Class, const TArray<STypeVariableSubstitution>& Substitutions)
{
if (Class.Definition()->_EnclosingScope.GetKind() != CScope::EKind::Function)
{
return nullptr;
}
TArray<STypeVariableSubstitution> InstTypeVariables = InstantiateTypeVariableSubstitutions(
Class._TypeVariableSubstitutions,
Substitutions);
CClass* GeneralizedClass = Class._GeneralizedClass;
TURefArray<CClass>& InstClasses = GeneralizedClass->_InstantiatedClasses;
if (CClass* InstClass = FindInstantiatedClass(InstClasses, InstTypeVariables))
{
return InstClass;
}
CClass* Superclass = Class._Superclass;
if (Superclass)
{
if (CClass* InstSuperclass = InstantiatePositiveClass(*Superclass, Substitutions))
{
Superclass = InstSuperclass;
}
}
int32_t I = InstClasses.AddNew(
Class.GetParentScope(),
Class._Definition,
Class._StructOrClass,
Superclass,
InstantiatePositiveInterfaces(Class._SuperInterfaces, Substitutions),
Class._ConstructorEffects,
GeneralizedClass,
Move(InstTypeVariables));
CClass* InstClass = InstClasses[I];
for (const CDefinition* Definition : Class.GetDefinitions())
{
if (const CDataDefinition* DataDefinition = Definition->AsNullable<CDataDefinition>())
{
InstantiatePositiveDataDefinition(*InstClass, *InstClass, *DataDefinition, Substitutions);
}
else if (const CFunction* Function = Definition->AsNullable<CFunction>())
{
InstantiatePositiveFunction(*InstClass, *InstClass, *Function, Substitutions);
}
}
CreateNegativeClassMemberDefinitions(*InstClass);
SetNegativeClassMemberDefinitionTypes(*InstClass);
return InstClass;
}
CClass* InstantiateClass(const CClass& Class, ETypePolarity Polarity, const TArray<STypeVariableSubstitution>& Substitutions)
{
switch (Polarity)
{
case ETypePolarity::Negative:
if (CClass* InstClass = InstantiatePositiveClass(*Class._NegativeClass, Substitutions))
{
return InstClass->_NegativeClass;
}
return nullptr;
case ETypePolarity::Positive: return InstantiatePositiveClass(Class, Substitutions);
default: ULANG_UNREACHABLE();
}
}
void SetNegativeClassMemberDefinitionTypes(const CClass& PositiveClass)
{
CClass* NegativeClass = PositiveClass._NegativeClass;
const TArray<TSRef<CDefinition>>& PositiveDefinitions = PositiveClass.GetDefinitions();
const TArray<TSRef<CDefinition>>& NegativeDefinitions = NegativeClass->GetDefinitions();
for (auto I = PositiveDefinitions.begin(), J = NegativeDefinitions.begin(), Last = PositiveDefinitions.end(); I != Last; ++I)
{
if (const CDataDefinition* PositiveDataDefinition = (*I)->AsNullable<CDataDefinition>())
{
CDataDefinition& NegativeDataDefinition = (*J)->AsChecked<CDataDefinition>();
NegativeDataDefinition._NegativeType = PositiveDataDefinition->GetType();
NegativeDataDefinition.SetType(PositiveDataDefinition->_NegativeType);
++J;
}
else if (const CFunction* PositiveFunction = (*I)->AsNullable<CFunction>())
{
SetNegativeMemberDefinitionType((*J)->AsChecked<CFunction>(), *PositiveFunction);
++J;
}
}
}
}