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

198 lines
6.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "uLang/Semantics/AccessibilityScope.h"
#include "uLang/Common/Text/UTF8StringBuilder.h"
#include "uLang/Semantics/AccessLevel.h"
#include "uLang/Semantics/Definition.h"
#include "uLang/Semantics/SemanticProgram.h"
#include "uLang/Semantics/SemanticScope.h"
#include "uLang/Semantics/SemanticTypes.h"
namespace uLang
{
CUTF8String SAccessibilityScope::Describe() const
{
CUTF8StringBuilder Result;
if (_SuperType)
{
Result.AppendFormat("from subtypes of %s, ", _SuperType->AsCode().AsCString());
}
// Expected context is: accessible <result of this function>
switch (_Kind)
{
case EKind::Universal: Result.Append("universally"); break;
case EKind::EpicInternal: Result.Append("from any Epic-internal path"); break;
case EKind::Scope:
{
if (_Scopes.IsEmpty())
{
Result.Append("from nowhere");
}
else
{
Result.Append("from subpaths of ");
const char* Separator = "";
for (const CScope* Scope : _Scopes)
{
Result.AppendFormat("%s%s", Separator, Scope->GetScopePath('/', CScope::EPathMode::PrefixSeparator).AsCString());
Separator = ", ";
}
}
}
break;
default:
ULANG_UNREACHABLE();
};
return Result.MoveToString();
}
bool SAccessibilityScope::IsSubsetOf(const SAccessibilityScope& Other) const
{
if (Other._SuperType)
{
if (!_SuperType || SemanticTypeUtils::IsSubtype(_SuperType, Other._SuperType))
{
return false;
}
}
switch (Other._Kind)
{
case SAccessibilityScope::EKind::Universal:
{
return true;
}
case SAccessibilityScope::EKind::EpicInternal:
{
switch (_Kind)
{
case SAccessibilityScope::EKind::Universal:
{
return false;
}
case SAccessibilityScope::EKind::EpicInternal:
{
return true;
}
case SAccessibilityScope::EKind::Scope:
{
for (const CScope* LhsScope : _Scopes)
{
if (!LhsScope->IsAuthoredByEpic())
{
return false;
}
}
return true;
}
default: ULANG_UNREACHABLE();
}
}
case SAccessibilityScope::EKind::Scope:
{
// Note: We assume here that Other can never be such set of scopes
// that fully contains all universal and/or epic_internal scopes
if (_Kind != SAccessibilityScope::EKind::Scope)
{
return false;
}
for (const CScope* LhsScope : _Scopes)
{
if (!Other._Scopes.ContainsByPredicate([LhsScope](const CScope* RhsScope) { return LhsScope->IsSameOrChildOf(RhsScope); }))
{
return false;
}
}
return true;
}
default: ULANG_UNREACHABLE();
}
}
SAccessibilityScope GetAccessibilityScope(const CDefinition& Definition, const SAccessLevel& InitialAccessLevel)
{
SAccessibilityScope Result;
auto ConstrainByAccessLevel = [&Result](const SAccessLevel& AccessLevel, const CScope& EnclosingScope)
{
if (AccessLevel._Kind == SAccessLevel::EKind::Protected)
{
ULANG_ASSERT(!Result._SuperType);
Result._SuperType = EnclosingScope.ScopeAsType();
ULANG_ASSERT(Result._SuperType);
}
// Use the inner-most definition in the path to the root scope that is internal or private
// to Result._Scope to the largest "fully accessible scope" for the definition.
if (Result._Scopes.IsEmpty())
{
if (AccessLevel._Kind == SAccessLevel::EKind::Private)
{
Result._Scopes.Add(&EnclosingScope);
}
else if (AccessLevel._Kind == SAccessLevel::EKind::Internal)
{
Result._Scopes.Add(EnclosingScope.GetModule());
}
else if (AccessLevel._Kind == SAccessLevel::EKind::Scoped)
{
Result._Scopes.Add(EnclosingScope.GetModule());
for (const CScope* ModuleScope : AccessLevel._Scopes)
{
Result._Scopes.AddUnique(ModuleScope);
}
}
else if (AccessLevel._Kind == SAccessLevel::EKind::EpicInternal)
{
// Remember that we encountered an EpicInternal scope at some point up to the first scope
Result._Kind = SAccessibilityScope::EKind::EpicInternal;
}
}
};
// Constrain the accessibility by the initial access level.
ConstrainByAccessLevel(InitialAccessLevel, Definition._EnclosingScope);
for (const CDefinition* DefinitionIt = &Definition; DefinitionIt; DefinitionIt = DefinitionIt->GetEnclosingDefinition())
{
// Constrain the accessibility by the definition's access level.
const CDefinition& ConstrainingDefinition = DefinitionIt->GetDefinitionAccessibilityRoot();
ConstrainByAccessLevel(ConstrainingDefinition.DerivedAccessLevel(), ConstrainingDefinition._EnclosingScope.GetLogicalScope());
}
if (!Result._Scopes.IsEmpty())
{
// Is this symbol accessible only from epic_internal scopes?
if (Result._Kind == SAccessibilityScope::EKind::EpicInternal)
{
// Yes, remove all scopes that are not in an epic_internal scope
Result._Scopes.RemoveAll([](const CScope* Scope) { return !Scope->IsAuthoredByEpic(); });
// If no scopes are left at this point it means the definition is entirely inaccessible
}
Result._Kind = SAccessibilityScope::EKind::Scope;
}
return Result;
}
SAccessibilityScope GetAccessibilityScope(const CDefinition& Definition)
{
return GetAccessibilityScope(Definition, SAccessLevel::EKind::Public);
}
SAccessibilityScope GetConstructorAccessibilityScope(const CClassDefinition& Class)
{
return GetAccessibilityScope(Class, Class.DerivedConstructorAccessLevel());
}
SAccessibilityScope GetConstructorAccessibilityScope(const CInterface& Interface)
{
return GetAccessibilityScope(Interface, Interface.DerivedConstructorAccessLevel());
}
} // namespace uLang