diff --git a/source/core/core.natvis b/source/core/core.natvis
index f2547b3fe60..b9e7009e48a 100644
--- a/source/core/core.natvis
+++ b/source/core/core.natvis
@@ -102,6 +102,14 @@
+
+ _offset == 0 ? nullptr : ($T1*)((char*)this + _offset)
+ {($T1*)0}
+ {($T1*)((char*)this + _offset)}
+
+ _offset == 0 ? nullptr : ($T1*)((char*)this + _offset)
+
+
diff --git a/source/core/slang-string.cpp b/source/core/slang-string.cpp
index e9804eaa8cd..a0612ccda85 100644
--- a/source/core/slang-string.cpp
+++ b/source/core/slang-string.cpp
@@ -727,6 +727,22 @@ UnownedStringSlice UnownedStringSlice::subString(Index idx, Index len) const
return UnownedStringSlice(m_begin + idx, m_begin + idx + len);
}
+int compare(UnownedStringSlice const& lhs, UnownedStringSlice const& rhs)
+{
+ auto lhsSize = lhs.getLength();
+ auto rhsSize = rhs.getLength();
+
+ auto lhsData = lhs.begin();
+ auto rhsData = rhs.begin();
+
+ auto sharedPrefixSize = std::min(lhsSize, rhsSize);
+ int sharedPrefixCmp = memcmp(lhsData, rhsData, sharedPrefixSize);
+ if (sharedPrefixCmp != 0)
+ return sharedPrefixCmp;
+
+ return int(lhsSize - rhsSize);
+}
+
bool UnownedStringSlice::operator==(ThisType const& other) const
{
// Note that memcmp is undefined when passed in null ptrs, so if we want to handle
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index 3da0db6b9bd..6d84a0c957d 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -218,6 +218,14 @@ struct SLANG_RT_API UnownedStringSlice
char const* m_end;
};
+/// Three-way comparison of string slices.
+///
+/// * Returns 0 if `lhs == rhs`
+/// * Returns a value < 0 if `lhs < rhs`
+/// * Returns a value > 0 if `lhs > rhs`
+///
+int compare(UnownedStringSlice const& lhs, UnownedStringSlice const& rhs);
+
// A more convenient way to make slices from *string literals*
template
SLANG_FORCE_INLINE UnownedStringSlice toSlice(const char (&in)[SIZE])
diff --git a/source/slang/slang-ast-base.h b/source/slang/slang-ast-base.h
index 5b77f9d53e3..ac963861aeb 100644
--- a/source/slang/slang-ast-base.h
+++ b/source/slang/slang-ast-base.h
@@ -767,7 +767,7 @@ class Decl : public DeclBase
/// method, which ensures that the `_prevInContainerWithSameName` fields
/// have been properly set for all declarations in that container.
///
- Decl* _prevInContainerWithSameName = nullptr;
+ FIDDLE() Decl* _prevInContainerWithSameName = nullptr;
bool isChecked(DeclCheckState state) const { return checkState >= state; }
void setCheckState(DeclCheckState state)
diff --git a/source/slang/slang-ast-decl.cpp b/source/slang/slang-ast-decl.cpp
index f37ebef48a5..4d9b8718f01 100644
--- a/source/slang/slang-ast-decl.cpp
+++ b/source/slang/slang-ast-decl.cpp
@@ -51,80 +51,167 @@ bool isInterfaceRequirement(Decl* decl)
}
//
-// ContainerDecl
+// ContainerDeclDirectMemberDecls
//
-List const& ContainerDecl::getDirectMemberDecls()
+void ContainerDeclDirectMemberDecls::_initForOnDemandDeserialization(
+ RefObject* deserializationContext,
+ void const* deserializationData,
+ Count declCount)
{
- return _directMemberDecls.decls;
+ SLANG_ASSERT(deserializationContext);
+ SLANG_ASSERT(deserializationData);
+
+ SLANG_ASSERT(!decls.getCount());
+ SLANG_ASSERT(!onDemandDeserialization.context);
+
+ onDemandDeserialization.context = deserializationContext;
+ onDemandDeserialization.data = deserializationData;
+
+ for (Index i = 0; i < declCount; ++i)
+ decls.add(nullptr);
}
-Count ContainerDecl::getDirectMemberDeclCount()
+void ContainerDeclDirectMemberDecls::_readAllSerializedDecls() const
{
- return _directMemberDecls.decls.getCount();
+ SLANG_ASSERT(isUsingOnDemandDeserialization());
+
+ // We start by querying each of the contained decls
+ // by index, which should cause the entire `decls`
+ // array to be filled in.
+ //
+ auto declCount = getDeclCount();
+ for (Index i = 0; i < declCount; ++i)
+ {
+ auto decl = getDecl(i);
+ SLANG_UNUSED(decl);
+ }
+
+ // At this point, we have loaded all the information
+ // that was in the serialized representation, and
+ // don't need to keep doing on-demand loading.
+ // Thus, we clear out the pointer to the serialized
+ // data (which will cause later calls to
+ // `isDoingOnDemandSerialization()` to return `false`).
+ //
+ // Note that we do *not* clear out the `context` pointer
+ // used for on-demand deserialization, because in the
+ // case where we are storing the members of a `ModuleDecl`,
+ // that context will hold the additional state needed to
+ // look up declarations by their mangled names, and we
+ // want to retain that state. The
+ // `isUsingOnDemandDeserializationForExports()` query
+ // is based on the `context` pointer only, so it will
+ // continue to return `true`.
+ //
+ onDemandDeserialization.data = nullptr;
+
+ _invalidateLookupAccelerators();
}
-Decl* ContainerDecl::getDirectMemberDecl(Index index)
+List const& ContainerDeclDirectMemberDecls::getDecls() const
{
- return _directMemberDecls.decls[index];
+ if (isUsingOnDemandDeserialization())
+ {
+ _readAllSerializedDecls();
+ }
+
+ return decls;
}
-Decl* ContainerDecl::getFirstDirectMemberDecl()
+Count ContainerDeclDirectMemberDecls::getDeclCount() const
{
- if (getDirectMemberDeclCount() == 0)
- return nullptr;
- return getDirectMemberDecl(0);
+ // Note: in the case of on-demand deserialization,
+ // the number of elements in the `decls` list
+ // will be correct, although one or more of the
+ // pointers in it might be null.
+ //
+ return decls.getCount();
}
-DeclsOfNameList ContainerDecl::getDirectMemberDeclsOfName(Name* name)
+Decl* ContainerDeclDirectMemberDecls::getDecl(Index index) const
{
- return DeclsOfNameList(findLastDirectMemberDeclOfName(name));
+ auto decl = decls[index];
+ if (!decl && isUsingOnDemandDeserialization())
+ {
+ decl = _readSerializedDeclAtIndex(index);
+ decls[index] = decl;
+ }
+ return decl;
}
-Decl* ContainerDecl::findLastDirectMemberDeclOfName(Name* name)
+Decl* ContainerDeclDirectMemberDecls::findLastDeclOfName(Name* name) const
{
- _ensureLookupAcceleratorsAreValid();
- if (auto found = _directMemberDecls.accelerators.mapNameToLastDeclOfThatName.tryGetValue(name))
- return *found;
+ if (isUsingOnDemandDeserialization())
+ {
+ if (auto found = accelerators.mapNameToLastDeclOfThatName.tryGetValue(name))
+ return *found;
+
+ Decl* decl = _readSerializedDeclsOfName(name);
+ accelerators.mapNameToLastDeclOfThatName.add(name, decl);
+ return decl;
+ }
+ else
+ {
+ _ensureLookupAcceleratorsAreValid();
+ if (auto found = accelerators.mapNameToLastDeclOfThatName.tryGetValue(name))
+ return *found;
+ }
return nullptr;
}
-Decl* ContainerDecl::getPrevDirectMemberDeclWithSameName(Decl* decl)
+Dictionary ContainerDeclDirectMemberDecls::getMapFromNameToLastDeclOfThatName() const
{
- SLANG_ASSERT(decl);
- SLANG_ASSERT(decl->parentDecl == this);
+ if (isUsingOnDemandDeserialization())
+ {
+ // If we have been using on-demand deserialization,
+ // then the `mapNameToLastDeclOfThatName` dictionary
+ // may not accurately reflect the contained declarations.
+ // We need to force all of the declarations to be
+ // deserialized immediately, which will also have
+ // the effect of invalidating the accelerators so
+ // that they can be rebuilt to contain complete information.
+ //
+ _readAllSerializedDecls();
+ }
_ensureLookupAcceleratorsAreValid();
- return decl->_prevInContainerWithSameName;
+ return accelerators.mapNameToLastDeclOfThatName;
}
-void ContainerDecl::addDirectMemberDecl(Decl* decl)
-{
- if (!decl)
- return;
- decl->parentDecl = this;
- _directMemberDecls.decls.add(decl);
+List const& ContainerDeclDirectMemberDecls::getTransparentDecls() const
+{
+ if (isUsingOnDemandDeserialization())
+ {
+ if (accelerators.filteredListOfTransparentDecls.getCount() == 0)
+ {
+ _readSerializedTransparentDecls();
+ }
+ }
+ else
+ {
+ _ensureLookupAcceleratorsAreValid();
+ }
+ return accelerators.filteredListOfTransparentDecls;
}
-List const& ContainerDecl::getTransparentDirectMemberDecls()
+bool ContainerDeclDirectMemberDecls::isUsingOnDemandDeserialization() const
{
- _ensureLookupAcceleratorsAreValid();
- return _directMemberDecls.accelerators.filteredListOfTransparentDecls;
+ return onDemandDeserialization.data != nullptr;
}
-bool ContainerDecl::_areLookupAcceleratorsValid()
+bool ContainerDeclDirectMemberDecls::_areLookupAcceleratorsValid() const
{
- return _directMemberDecls.accelerators.declCountWhenLastUpdated ==
- _directMemberDecls.decls.getCount();
+ return accelerators.declCountWhenLastUpdated == decls.getCount();
}
-void ContainerDecl::_invalidateLookupAccelerators()
+void ContainerDeclDirectMemberDecls::_invalidateLookupAccelerators() const
{
- _directMemberDecls.accelerators.declCountWhenLastUpdated = -1;
+ accelerators.declCountWhenLastUpdated = -1;
}
-void ContainerDecl::_ensureLookupAcceleratorsAreValid()
+void ContainerDeclDirectMemberDecls::_ensureLookupAcceleratorsAreValid() const
{
if (_areLookupAcceleratorsValid())
return;
@@ -133,24 +220,28 @@ void ContainerDecl::_ensureLookupAcceleratorsAreValid()
// the accelerators are entirely invalidated, and must be rebuilt
// from scratch.
//
- if (_directMemberDecls.accelerators.declCountWhenLastUpdated < 0)
+ if (accelerators.declCountWhenLastUpdated < 0)
{
- _directMemberDecls.accelerators.declCountWhenLastUpdated = 0;
- _directMemberDecls.accelerators.mapNameToLastDeclOfThatName.clear();
- _directMemberDecls.accelerators.filteredListOfTransparentDecls.clear();
+ accelerators.declCountWhenLastUpdated = 0;
+ accelerators.mapNameToLastDeclOfThatName.clear();
+ accelerators.filteredListOfTransparentDecls.clear();
}
- // are we a generic?
- GenericDecl* genericDecl = as(this);
-
- Count memberCount = _directMemberDecls.decls.getCount();
- Count memberCountWhenLastUpdated = _directMemberDecls.accelerators.declCountWhenLastUpdated;
+ Count memberCount = decls.getCount();
+ Count memberCountWhenLastUpdated = accelerators.declCountWhenLastUpdated;
SLANG_ASSERT(memberCountWhenLastUpdated >= 0 && memberCountWhenLastUpdated <= memberCount);
+ // are we a generic?
+ GenericDecl* genericDecl = nullptr;
+ if (memberCount > 0)
+ {
+ genericDecl = as(decls[0]->parentDecl);
+ }
+
for (Index i = memberCountWhenLastUpdated; i < memberCount; ++i)
{
- Decl* memberDecl = _directMemberDecls.decls[i];
+ Decl* memberDecl = decls[i];
// Transparent member declarations will go into a separate list,
// so that they can be conveniently queried later for lookup
@@ -163,7 +254,7 @@ void ContainerDecl::_ensureLookupAcceleratorsAreValid()
//
if (memberDecl->hasModifier())
{
- _directMemberDecls.accelerators.filteredListOfTransparentDecls.add(memberDecl);
+ accelerators.filteredListOfTransparentDecls.add(memberDecl);
}
// Members that don't have a name don't go into the lookup dictionary.
@@ -190,9 +281,7 @@ void ContainerDecl::_ensureLookupAcceleratorsAreValid()
// all of the overloaded functions with a given name.
//
Decl* prevMemberWithSameName = nullptr;
- _directMemberDecls.accelerators.mapNameToLastDeclOfThatName.tryGetValue(
- memberName,
- prevMemberWithSameName);
+ accelerators.mapNameToLastDeclOfThatName.tryGetValue(memberName, prevMemberWithSameName);
memberDecl->_prevInContainerWithSameName = prevMemberWithSameName;
// Whether or not there was a previous declaration with this
@@ -200,18 +289,131 @@ void ContainerDecl::_ensureLookupAcceleratorsAreValid()
// with that name encountered so far, and it is what we will
// store in the lookup dictionary.
//
- _directMemberDecls.accelerators.mapNameToLastDeclOfThatName[memberName] = memberDecl;
+ accelerators.mapNameToLastDeclOfThatName[memberName] = memberDecl;
}
- _directMemberDecls.accelerators.declCountWhenLastUpdated = memberCount;
+ accelerators.declCountWhenLastUpdated = memberCount;
SLANG_ASSERT(_areLookupAcceleratorsValid());
}
+
+//
+// ContainerDecl
+//
+
+List const& ContainerDecl::getDirectMemberDecls()
+{
+ return _directMemberDecls.getDecls();
+}
+
+Count ContainerDecl::getDirectMemberDeclCount()
+{
+ return _directMemberDecls.getDeclCount();
+}
+
+Decl* ContainerDecl::getDirectMemberDecl(Index index)
+{
+ return _directMemberDecls.getDecl(index);
+}
+
+Decl* ContainerDecl::getFirstDirectMemberDecl()
+{
+ if (getDirectMemberDeclCount() == 0)
+ return nullptr;
+ return getDirectMemberDecl(0);
+}
+
+DeclsOfNameList ContainerDecl::getDirectMemberDeclsOfName(Name* name)
+{
+ return DeclsOfNameList(findLastDirectMemberDeclOfName(name));
+}
+
+Decl* ContainerDecl::findLastDirectMemberDeclOfName(Name* name)
+{
+ return _directMemberDecls.findLastDeclOfName(name);
+}
+
+Decl* ContainerDecl::getPrevDirectMemberDeclWithSameName(Decl* decl)
+{
+ SLANG_ASSERT(decl);
+ SLANG_ASSERT(decl->parentDecl == this);
+
+ if (isUsingOnDemandDeserializationForDirectMembers())
+ {
+ // Note: in the case of on-demand deserialization,
+ // we trust that the caller has previously
+ // invoked `findLastDirectMemberDeclOfName()`
+ // in order to get `decl` (or an earlier
+ // entry in the same linked list), so that
+ // the list threaded through the declarations
+ // of the same name is already set up.
+ //
+ // If that is ever *not* the case, then this
+ // query would end up returning the wrong results.
+
+ return decl->_prevInContainerWithSameName;
+ }
+ else
+ {
+ _ensureLookupAcceleratorsAreValid();
+ return decl->_prevInContainerWithSameName;
+ }
+}
+
+void ContainerDecl::addDirectMemberDecl(Decl* decl)
+{
+ if (isUsingOnDemandDeserializationForDirectMembers())
+ {
+ SLANG_UNEXPECTED("this operation shouldn't be performed on deserialized declarations");
+ }
+
+ if (!decl)
+ return;
+
+ decl->parentDecl = this;
+ _directMemberDecls.decls.add(decl);
+}
+
+List const& ContainerDecl::getTransparentDirectMemberDecls()
+{
+ return _directMemberDecls.getTransparentDecls();
+}
+
+bool ContainerDecl::isUsingOnDemandDeserializationForDirectMembers()
+{
+ return _directMemberDecls.isUsingOnDemandDeserialization();
+}
+
+bool ModuleDecl::isUsingOnDemandDeserializationForExports()
+{
+ return _directMemberDecls.onDemandDeserialization.context != nullptr;
+}
+
+bool ContainerDecl::_areLookupAcceleratorsValid()
+{
+ return _directMemberDecls._areLookupAcceleratorsValid();
+}
+
+void ContainerDecl::_invalidateLookupAccelerators()
+{
+ _directMemberDecls._invalidateLookupAccelerators();
+}
+
+void ContainerDecl::_ensureLookupAcceleratorsAreValid()
+{
+ _directMemberDecls._ensureLookupAcceleratorsAreValid();
+}
+
void ContainerDecl::
_invalidateLookupAcceleratorsBecauseUnscopedEnumAttributeWillBeTurnedIntoTransparentModifier(
UnscopedEnumAttribute* unscopedEnumAttr,
TransparentModifier* transparentModifier)
{
+ if (isUsingOnDemandDeserializationForDirectMembers())
+ {
+ SLANG_UNEXPECTED("this operation shouldn't be performed on deserialized declarations");
+ }
+
SLANG_ASSERT(unscopedEnumAttr);
SLANG_ASSERT(transparentModifier);
@@ -225,6 +427,11 @@ void ContainerDecl::
_removeDirectMemberConstructorDeclBecauseSynthesizedAnotherDefaultConstructorInstead(
ConstructorDecl* decl)
{
+ if (isUsingOnDemandDeserializationForDirectMembers())
+ {
+ SLANG_UNEXPECTED("this operation shouldn't be performed on deserialized declarations");
+ }
+
SLANG_ASSERT(decl);
_invalidateLookupAccelerators();
@@ -237,6 +444,11 @@ void ContainerDecl::
VarDecl* oldDecl,
PropertyDecl* newDecl)
{
+ if (isUsingOnDemandDeserializationForDirectMembers())
+ {
+ SLANG_UNEXPECTED("this operation shouldn't be performed on deserialized declarations");
+ }
+
SLANG_ASSERT(oldDecl);
SLANG_ASSERT(newDecl);
SLANG_ASSERT(index >= 0 && index < getDirectMemberDeclCount());
@@ -251,6 +463,11 @@ void ContainerDecl::_insertDirectMemberDeclAtIndexForBitfieldPropertyBackingMemb
Index index,
VarDecl* backingVarDecl)
{
+ if (isUsingOnDemandDeserializationForDirectMembers())
+ {
+ SLANG_UNEXPECTED("this operation shouldn't be performed on deserialized declarations");
+ }
+
SLANG_ASSERT(backingVarDecl);
SLANG_ASSERT(index >= 0 && index <= getDirectMemberDeclCount());
diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h
index c46878945d7..a92f73e2a9e 100644
--- a/source/slang/slang-ast-decl.h
+++ b/source/slang/slang-ast-decl.h
@@ -4,6 +4,7 @@
#include "slang-ast-base.h"
#include "slang-ast-decl.h.fiddle"
+#include "slang-fossil.h"
FIDDLE()
namespace Slang
@@ -34,23 +35,54 @@ class UnresolvedDecl : public Decl
struct ContainerDeclDirectMemberDecls
{
public:
- List const& getDecls() const { return decls; }
+ List const& getDecls() const;
- List& _refDecls() { return decls; }
+ Count getDeclCount() const;
+ Decl* getDecl(Index index) const;
+
+ Decl* findLastDeclOfName(Name* name) const;
+
+ Dictionary getMapFromNameToLastDeclOfThatName() const;
+
+ List const& getTransparentDecls() const;
+
+ bool isUsingOnDemandDeserialization() const;
+
+ void _initForOnDemandDeserialization(
+ RefObject* deserializationContext,
+ void const* deserializationData,
+ Count declCount);
private:
friend class ContainerDecl;
+ friend class ModuleDecl;
friend struct ASTDumpContext;
- List decls;
+ bool _areLookupAcceleratorsValid() const;
+ void _invalidateLookupAccelerators() const;
+ void _ensureLookupAcceleratorsAreValid() const;
+
+ void _readSerializedTransparentDecls() const;
+ Decl* _readSerializedDeclAtIndex(Index index) const;
+ Decl* _readSerializedDeclsOfName(Name* name) const;
+
+ void _readAllSerializedDecls() const;
+
+ mutable List decls;
- struct
+ mutable struct
{
Count declCountWhenLastUpdated = 0;
Dictionary mapNameToLastDeclOfThatName;
List filteredListOfTransparentDecls;
} accelerators;
+
+ mutable struct
+ {
+ RefPtr context;
+ void const* data = nullptr;
+ } onDemandDeserialization;
};
/// A conceptual list of declarations of the same name, in the same container.
@@ -199,6 +231,10 @@ class ContainerDecl : public Decl
//
void addMember(Decl* member) { addDirectMemberDecl(member); }
+ /// Is this declaration using on-demand deserialization for its direct members?
+ ///
+ bool isUsingOnDemandDeserializationForDirectMembers();
+
//
// NOTE: The operations after this point are *not* considered part of
// the public API of `ContainerDecl`, and new code should not be
@@ -737,6 +773,14 @@ class ModuleDecl : public NamespaceDeclBase
/// This mapping is filled in during semantic checking, as `ExtensionDecl`s get checked.
///
FIDDLE() Dictionary> mapTypeToCandidateExtensions;
+
+ /// Is this module using on-demand deserialization for its exports?
+ ///
+ bool isUsingOnDemandDeserializationForExports();
+
+ /// Find a declaration exported from this module by its `mangledName`.
+ ///
+ Decl* _findSerializedDeclByMangledExportName(UnownedStringSlice const& mangledName);
};
// Represents a transparent scope of declarations that are defined in a single source file.
diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h
index 177feff1577..d6027b2b26f 100644
--- a/source/slang/slang-ast-expr.h
+++ b/source/slang/slang-ast-expr.h
@@ -309,13 +309,17 @@ class StaticMemberExpr : public DeclRefExpr
SourceLoc memberOperatorLoc;
};
+FIDDLE()
struct MatrixCoord
{
+ FIDDLE(...)
+
bool operator==(const MatrixCoord& rhs) const { return row == rhs.row && col == rhs.col; };
bool operator!=(const MatrixCoord& rhs) const { return !(*this == rhs); };
+
// Rows and columns are zero indexed
- int row;
- int col;
+ FIDDLE() Int32 row;
+ FIDDLE() Int32 col;
};
FIDDLE()
@@ -851,40 +855,41 @@ struct SPIRVAsmOperand
};
// The flavour and token describes how this was parsed
- Flavor flavor;
+ FIDDLE() Flavor flavor;
// The single token this came from
Token token;
// If this was a SlangValue or SlangValueAddr or SlangType, then we also
// store the expression, which should be a single VarExpr because we only
// parse single idents at the moment
- Expr* expr = nullptr;
+ FIDDLE() Expr* expr = nullptr;
// If this is part of a bitwise or expression, this will point to the
// remaining operands values in such an expression must be of flavour
// Literal or NamedValue
- List bitwiseOrWith = List();
+ FIDDLE() List bitwiseOrWith = List();
// If this is a named value then we calculate the value here during
// checking. If this is an opcode, then the parser will populate this too
// (or set it to 0xffffffff);
- SpvWord knownValue = 0xffffffff;
+ FIDDLE() SpvWord knownValue = 0xffffffff;
// Although this might be a constant in the source we should actually pass
// it as an id created with OpConstant
- bool wrapInId = false;
+ FIDDLE() bool wrapInId = false;
// Once we've checked things, the SlangType and BuiltinVar flavour operands
// will have this type populated.
- TypeExp type = TypeExp();
+ FIDDLE() TypeExp type = TypeExp();
};
FIDDLE()
struct SPIRVAsmInst
{
FIDDLE(...)
+
public:
- SPIRVAsmOperand opcode;
- List operands;
+ FIDDLE() SPIRVAsmOperand opcode;
+ FIDDLE() List operands;
};
FIDDLE()
diff --git a/source/slang/slang-ast-forward-declarations.h b/source/slang/slang-ast-forward-declarations.h
index 717bca1d970..32cfd9ef03e 100644
--- a/source/slang/slang-ast-forward-declarations.h
+++ b/source/slang/slang-ast-forward-declarations.h
@@ -1,10 +1,12 @@
// slang-ast-forward-declarations.h
#pragma once
+#include "../core/slang-basic.h"
+
namespace Slang
{
-enum class ASTNodeType
+enum class ASTNodeType : Int32
{
#if 0 // FIDDLE TEMPLATE:
%for _, T in ipairs(Slang.NodeBase.subclasses) do
diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h
index d05f24e56fe..e4002c23737 100644
--- a/source/slang/slang-ast-support-types.h
+++ b/source/slang/slang-ast-support-types.h
@@ -232,10 +232,12 @@ FIDDLE() namespace Slang
class Val;
// Helper type for pairing up a name and the location where it appeared
- struct NameLoc
+ FIDDLE() struct NameLoc
{
- Name* name;
- SourceLoc loc;
+ FIDDLE(...)
+
+ FIDDLE() Name* name;
+ FIDDLE() SourceLoc loc;
NameLoc()
: name(nullptr)
@@ -572,10 +574,11 @@ FIDDLE() namespace Slang
struct QualType
{
FIDDLE(...)
- Type* type = nullptr;
- bool isLeftValue = false;
- bool hasReadOnlyOnTarget = false;
- bool isWriteOnly = false;
+
+ FIDDLE() Type* type = nullptr;
+ FIDDLE() bool isLeftValue = false;
+ FIDDLE() bool hasReadOnlyOnTarget = false;
+ FIDDLE() bool isWriteOnly = false;
QualType() = default;
@@ -1571,16 +1574,16 @@ FIDDLE() namespace Slang
void add(Decl* decl, RequirementWitness const& witness);
// The type that the witness table witnesses conformance to (e.g. an Interface)
- Type* baseType;
+ FIDDLE() Type* baseType;
// The type witnessesd by the witness table (a concrete type).
- Type* witnessedType;
+ FIDDLE() Type* witnessedType;
// Whether or not this witness table is an extern declaration.
- bool isExtern = false;
+ FIDDLE() bool isExtern = false;
// Cached dictionary for looking up satisfying values.
- RequirementDictionary m_requirementDictionary;
+ FIDDLE() RequirementDictionary m_requirementDictionary;
RefPtr specialize(ASTBuilder* astBuilder, SubstitutionSet const& subst);
};
@@ -1634,8 +1637,9 @@ FIDDLE() namespace Slang
class DeclAssociation : public RefObject
{
FIDDLE(...)
- DeclAssociationKind kind;
- Decl* decl;
+
+ FIDDLE() DeclAssociationKind kind;
+ FIDDLE() Decl* decl;
};
/// A reference-counted object to hold a list of associated decls for a decl.
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index f0cd32e74f2..1a70e25d725 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -3494,14 +3494,8 @@ struct SemanticsDeclDifferentialConformanceVisitor
}
};
-/// Recursively register any builtin declarations that need to be attached to the `session`.
-///
-/// This function should only be needed for declarations in the core module.
-///
-static void _registerBuiltinDeclsRec(Session* session, Decl* decl)
+void registerBuiltinDecl(SharedASTBuilder* sharedASTBuilder, Decl* decl)
{
- SharedASTBuilder* sharedASTBuilder = session->m_sharedASTBuilder;
-
if (auto builtinMod = decl->findModifier())
{
sharedASTBuilder->registerBuiltinDecl(decl, builtinMod);
@@ -3514,6 +3508,25 @@ static void _registerBuiltinDeclsRec(Session* session, Decl* decl)
{
sharedASTBuilder->registerBuiltinRequirementDecl(decl, builtinRequirement);
}
+}
+
+
+void registerBuiltinDecl(ASTBuilder* astBuilder, Decl* decl)
+{
+ registerBuiltinDecl(astBuilder->getSharedASTBuilder(), decl);
+}
+
+
+/// Recursively register any builtin declarations that need to be attached to the `session`.
+///
+/// This function should only be needed for declarations in the core module.
+///
+static void _registerBuiltinDeclsRec(Session* session, Decl* decl)
+{
+ SharedASTBuilder* sharedASTBuilder = session->m_sharedASTBuilder;
+
+ registerBuiltinDecl(sharedASTBuilder, decl);
+
if (auto containerDecl = as(decl))
{
for (auto childDecl : containerDecl->getDirectMemberDecls())
@@ -3535,6 +3548,42 @@ void registerBuiltinDecls(Session* session, Decl* decl)
_registerBuiltinDeclsRec(session, decl);
}
+void _collectBuiltinDeclsThatNeedRegistrationRec(Decl* decl, List& ioDecls)
+{
+ if (decl->findModifier())
+ {
+ ioDecls.add(decl);
+ }
+ else if (decl->findModifier())
+ {
+ ioDecls.add(decl);
+ }
+ else if (decl->findModifier())
+ {
+ ioDecls.add(decl);
+ }
+
+ if (auto containerDecl = as(decl))
+ {
+ for (auto childDecl : containerDecl->getDirectMemberDecls())
+ {
+ if (as(childDecl))
+ continue;
+
+ _collectBuiltinDeclsThatNeedRegistrationRec(childDecl, ioDecls);
+ }
+ }
+ if (auto genericDecl = as(decl))
+ {
+ _collectBuiltinDeclsThatNeedRegistrationRec(genericDecl->inner, ioDecls);
+ }
+}
+
+void collectBuiltinDeclsThatNeedRegistration(ModuleDecl* moduleDecl, List& outDecls)
+{
+ _collectBuiltinDeclsThatNeedRegistrationRec(moduleDecl, outDecls);
+}
+
Type* unwrapArrayType(Type* type)
{
for (;;)
diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp
index d135744afef..7779957ef4d 100644
--- a/source/slang/slang-check-modifier.cpp
+++ b/source/slang/slang-check-modifier.cpp
@@ -2225,7 +2225,6 @@ void SemanticsVisitor::checkRayPayloadStructFields(StructDecl* structDecl)
{
auto readModifier = fieldVarDecl->findModifier();
auto writeModifier = fieldVarDecl->findModifier();
-
bool hasReadModifier = readModifier != nullptr;
bool hasWriteModifier = writeModifier != nullptr;
diff --git a/source/slang/slang-check.h b/source/slang/slang-check.h
index f4d86cff93e..ebeff1afeaf 100644
--- a/source/slang/slang-check.h
+++ b/source/slang/slang-check.h
@@ -21,8 +21,13 @@ class TranslationUnitRequest;
bool isGlobalShaderParameter(VarDeclBase* decl);
bool isFromCoreModule(Decl* decl);
+void registerBuiltinDecl(SharedASTBuilder* sharedASTBuilder, Decl* decl);
+void registerBuiltinDecl(ASTBuilder* astBuilder, Decl* decl);
+
void registerBuiltinDecls(Session* session, Decl* decl);
+void collectBuiltinDeclsThatNeedRegistration(ModuleDecl* moduleDecl, List& outDecls);
+
Type* unwrapArrayType(Type* type);
Type* unwrapModifiedType(Type* type);
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 43b51b23165..cee80f8828d 100644
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -1794,7 +1794,16 @@ class Module : public ComponentType, public slang::IModule
/// Given a mangled name finds the exported NodeBase associated with this module.
/// If not found returns nullptr.
- NodeBase* findExportFromMangledName(const UnownedStringSlice& slice);
+ Decl* findExportedDeclByMangledName(const UnownedStringSlice& mangledName);
+
+ /// Ensure that the any accelerator(s) used for `findExportedDeclByMangledName`
+ /// have already been built.
+ ///
+ void ensureExportLookupAcceleratorBuilt();
+
+ Count getExportedDeclCount();
+ Decl* getExportedDecl(Index index);
+ UnownedStringSlice getExportedDeclMangledName(Index index);
/// Get the ASTBuilder
ASTBuilder* getASTBuilder() { return m_astBuilder; }
@@ -1906,7 +1915,7 @@ class Module : public ComponentType, public slang::IModule
// Holds map of exported mangled names to symbols. m_mangledExportPool maps names to indices,
// and m_mangledExportSymbols holds the NodeBase* values for each index.
StringSlicePool m_mangledExportPool;
- List m_mangledExportSymbols;
+ List m_mangledExportSymbols;
// Source files that have been pulled into the module with `__include`.
Dictionary m_mapSourceFileToFileDecl;
@@ -2451,6 +2460,7 @@ class Linkage : public RefObject, public slang::ISession
/// Otherwise, return null.
///
RefPtr findOrLoadSerializedModuleForModuleLibrary(
+ ISlangBlob* blobHoldingSerializedData,
ModuleChunk const* moduleChunk,
RIFF::ListChunk const* libraryChunk,
DiagnosticSink* sink);
@@ -2458,6 +2468,7 @@ class Linkage : public RefObject, public slang::ISession
RefPtr loadSerializedModule(
Name* moduleName,
const PathInfo& moduleFilePathInfo,
+ ISlangBlob* blobHoldingSerializedData,
ModuleChunk const* moduleChunk,
RIFF::ListChunk const* containerChunk, //< The outer container, if there is one.
SourceLoc const& requestingLoc,
@@ -2466,6 +2477,7 @@ class Linkage : public RefObject, public slang::ISession
SlangResult loadSerializedModuleContents(
Module* module,
const PathInfo& moduleFilePathInfo,
+ ISlangBlob* blobHoldingSerializedData,
ModuleChunk const* moduleChunk,
RIFF::ListChunk const* containerChunk, //< The outer container, if there is one.
DiagnosticSink* sink);
diff --git a/source/slang/slang-fossil.cpp b/source/slang/slang-fossil.cpp
index e3b2e2c9d7e..20443090193 100644
--- a/source/slang/slang-fossil.cpp
+++ b/source/slang/slang-fossil.cpp
@@ -25,12 +25,12 @@ const char Fossil::Header::kMagic[16] = {
'\n' // byte 15
};
-FossilizedValRef getRootValue(ISlangBlob* blob)
+Fossil::AnyValPtr getRootValue(ISlangBlob* blob)
{
return getRootValue(blob->getBufferPointer(), blob->getBufferSize());
}
-FossilizedValRef getRootValue(void const* data, Size size)
+Fossil::AnyValPtr getRootValue(void const* data, Size size)
{
if (!data)
{
@@ -72,9 +72,7 @@ FossilizedValRef getRootValue(void const* data, Size size)
SLANG_UNEXPECTED("bad format for fossil");
}
- return FossilizedValRef(
- rootValueVariant->getContentData(),
- rootValueVariant->getContentLayout());
+ return getVariantContentPtr(rootValueVariant);
}
} // namespace Fossil
@@ -85,13 +83,13 @@ Size FossilizedStringObj::getSize() const
return Size(*sizePtr);
}
-UnownedTerminatedStringSlice FossilizedStringObj::getValue() const
+UnownedTerminatedStringSlice FossilizedStringObj::get() const
{
auto size = getSize();
return UnownedTerminatedStringSlice((char*)this, size);
}
-Count FossilizedContainerObj::getElementCount() const
+Count FossilizedContainerObjBase::getElementCount() const
{
auto countPtr = (FossilUInt*)this - 1;
return Size(*countPtr);
@@ -103,52 +101,18 @@ FossilizedValLayout* FossilizedVariantObj::getContentLayout() const
return (*layoutPtrPtr).get();
}
-FossilizedValRef getPtrTarget(FossilizedPtrValRef ptrRef)
-{
- auto ptrLayout = ptrRef.getLayout();
- auto ptrPtr = ptrRef.getData();
- return FossilizedValRef(ptrPtr->getTargetData(), ptrLayout->elementLayout);
-}
-
-bool hasValue(FossilizedOptionalObjRef optionalRef)
-{
- return optionalRef.getData() != nullptr;
-}
-
-FossilizedValRef getValue(FossilizedOptionalObjRef optionalRef)
-{
- auto optionalLayout = optionalRef.getLayout();
- auto valuePtr = optionalRef.getData();
- return FossilizedValRef(valuePtr, optionalLayout->elementLayout);
-}
-
-Count getElementCount(FossilizedContainerObjRef containerRef)
-{
- if (!containerRef)
- return 0;
-
- auto containerPtr = containerRef.getData();
- return containerPtr->getElementCount();
-}
-
-FossilizedValRef getElement(FossilizedContainerObjRef containerRef, Index index)
+Fossil::AnyValRef Fossil::ValRef::getElement(Index index) const
{
SLANG_ASSERT(index >= 0);
- SLANG_ASSERT(index < getElementCount(containerRef));
+ SLANG_ASSERT(index < getElementCount());
- auto containerLayout = containerRef.getLayout();
+ auto containerLayout = getLayout();
auto elementLayout = containerLayout->elementLayout.get();
auto elementStride = containerLayout->elementStride;
- auto elementsPtr = (Byte*)containerRef.getData();
- auto elementPtr = (FossilizedVal*)(elementsPtr + elementStride * index);
- return FossilizedValRef(elementPtr, elementLayout);
-}
-
-Count getFieldCount(FossilizedRecordValRef recordRef)
-{
- auto recordLayout = recordRef.getLayout();
- return recordLayout->fieldCount;
+ auto elementsPtr = (Byte*)getDataPtr();
+ auto elementPtr = (void*)(elementsPtr + elementStride * index);
+ return Fossil::AnyValRef(elementPtr, elementLayout);
}
FossilizedRecordElementLayout* FossilizedRecordLayout::getField(Index index) const
@@ -160,28 +124,29 @@ FossilizedRecordElementLayout* FossilizedRecordLayout::getField(Index index) con
return fieldsPtr + index;
}
-
-FossilizedValRef getField(FossilizedRecordValRef recordRef, Index index)
+Fossil::AnyValRef Fossil::ValRef::getField(Index index) const
{
SLANG_ASSERT(index >= 0);
- SLANG_ASSERT(index < getFieldCount(recordRef));
+ SLANG_ASSERT(index < getFieldCount());
- auto recordLayout = recordRef.getLayout();
- auto field = recordLayout->getField(index);
+ auto recordLayout = getLayout();
+ auto fieldInfo = recordLayout->getField(index);
- auto fieldsPtr = (Byte*)recordRef.getData();
- auto fieldPtr = (FossilizedVal*)(fieldsPtr + field->offset);
- return FossilizedValRef(fieldPtr, field->layout);
+ auto fieldsPtr = (Byte*)getDataPtr();
+ auto fieldPtr = (void*)(fieldsPtr + fieldInfo->offset);
+ return Fossil::AnyValRef(fieldPtr, fieldInfo->layout);
}
+#if 0
FossilizedValRef getVariantContent(FossilizedVariantObjRef variantRef)
{
return getVariantContent(variantRef.getData());
}
+#endif
-FossilizedValRef getVariantContent(FossilizedVariantObj* variantPtr)
+Fossil::AnyValPtr getVariantContentPtr(FossilizedVariantObj* variantPtr)
{
- return FossilizedValRef(variantPtr->getContentData(), variantPtr->getContentLayout());
+ return Fossil::AnyValPtr(variantPtr->getContentDataPtr(), variantPtr->getContentLayout());
}
} // namespace Slang
diff --git a/source/slang/slang-fossil.h b/source/slang/slang-fossil.h
index dcc12bacb3c..8d2465ddbd2 100644
--- a/source/slang/slang-fossil.h
+++ b/source/slang/slang-fossil.h
@@ -18,73 +18,765 @@
#include "../core/slang-relative-ptr.h"
+#include
+#include
+
namespace Slang
{
+
+struct FossilizedPtrLikeLayout;
+struct FossilizedRecordLayout;
+struct FossilizedValLayout;
+
+using FossilInt = int32_t;
+using FossilUInt = uint32_t;
+
+/// Kinds of values that can appear in fossilized data.
+enum class FossilizedValKind : FossilUInt
+{
+ Bool,
+ Int8,
+ Int16,
+ Int32,
+ Int64,
+ UInt8,
+ UInt16,
+ UInt32,
+ UInt64,
+ Float32,
+ Float64,
+ StringObj,
+ ArrayObj,
+ OptionalObj,
+ DictionaryObj,
+ Tuple,
+ Struct,
+ Ptr,
+ VariantObj,
+};
+
// A key part of the fossil representation is the use of *relative pointers*,
// so that a fossilized object graph can be traversed dirctly in memory
// without having to deserialize any of the intermediate objects.
//
-// Fossil uses 32-bit relative pointers, to keep the format compact.
+// Fossil uses 32-bit relative pointers, to keep the format compact.
+
+template
+struct FossilizedPtr : RelativePtr32
+{
+public:
+ using RelativePtr32::RelativePtr32;
+
+ using Layout = FossilizedPtrLikeLayout;
+
+ static bool isMatchingKind(FossilizedValKind kind) { return kind == FossilizedValKind::Ptr; }
+};
+
+static_assert(sizeof(FossilizedPtr) == sizeof(uint32_t));
+
+// Various other parts of the format need to store offsets or counts,
+// and for consistency we will store them with the same number of
+// bits as the relative pointers already used in the format.
+//
+// To make it easier for us to (potentially) change the relative
+// pointer size down the line, we define type aliases for the
+// general-purpose integer types that will be used in fossilized data.
+
+
+static_assert(sizeof(FossilInt) == sizeof(FossilizedPtr));
+static_assert(sizeof(FossilUInt) == sizeof(FossilizedPtr));
+
+//
+// A "live" type can declare what its fossilized representation
+// is by specializing the `FossilizedTypeTraits` template.
+//
+// By default, a type is fossilized as an opaque `FossilizedOpaqueVal`
+// if no user-defined specialization is provided.
+//
+
+template
+struct Fossilized_;
+
+template
+struct FossilizedTypeTraits
+{
+ using FossilizedType = Fossilized_;
+};
+
+template
+using Fossilized = FossilizedTypeTraits::FossilizedType;
+
+//
+// In many cases, a new C++ type can be fossilized using
+// the same representation as some existing type, so we
+// allow them to conveniently declare that fact with
+// a macro.
+//
+
+#define SLANG_DECLARE_FOSSILIZED_AS(TYPE, FOSSILIZED_AS) \
+ template<> \
+ struct FossilizedTypeTraits \
+ { \
+ using FossilizedType = Fossilized; \
+ }
+
+//
+// Another common pattern is when some aggregate type
+// can simply be fossilized as one of its members.
+//
+
+#define SLANG_DECLARE_FOSSILIZED_AS_MEMBER(TYPE, MEMBER) \
+ template<> \
+ struct FossilizedTypeTraits \
+ { \
+ using FossilizedType = Fossilized; \
+ }
+
+//
+// Simple scalar values are fossilized into a wrapper
+// `struct` that contains the underlying value.
+//
+// The reason to impose the wrapper `struct` is that
+// it allows us to control the alignment of the type
+// in case it turns out that different targets/compilers
+// don't apply the same layout to all of the underlying
+// scalar types.
+//
+
+template
+struct FossilizedSimpleVal
+{
+public:
+ using Layout = FossilizedValLayout;
+ static const FossilizedValKind kKind = Kind;
+
+ T const& get() const { return _value; }
+
+ operator T const&() const { return _value; }
+
+ static bool isMatchingKind(FossilizedValKind kind) { return kind == kKind; }
+
+private:
+ T _value;
+};
+
+#define SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(TYPE, TAG) \
+ template<> \
+ struct FossilizedTypeTraits \
+ { \
+ using FossilizedType = FossilizedSimpleVal; \
+ };
+
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(int8_t, Int8)
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(int16_t, Int16)
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(int32_t, Int32)
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(int64_t, Int64)
+
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(uint8_t, UInt8)
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(uint16_t, UInt16)
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(uint32_t, UInt32)
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(uint64_t, UInt64)
+
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(float, Float32)
+SLANG_DECLARE_FOSSILIZED_SIMPLE_TYPE(double, Float64)
+
+static_assert(sizeof(Fossilized) == 1);
+static_assert(sizeof(Fossilized) == 2);
+static_assert(sizeof(Fossilized) == 4);
+static_assert(sizeof(Fossilized) == 8);
+
+static_assert(sizeof(Fossilized) == 1);
+static_assert(sizeof(Fossilized) == 2);
+static_assert(sizeof(Fossilized) == 4);
+static_assert(sizeof(Fossilized) == 8);
+
+static_assert(sizeof(Fossilized) == 4);
+static_assert(sizeof(Fossilized) == 8);
+
+//
+// The `bool` type shouldn't be fossilized as itself, because
+// its layout is not guaranteed to be consistent across targets.
+// We instead fossilize it as an underlying `uint8_t`, and convert
+// on reads.
+//
+
+template<>
+struct Fossilized_
+{
+public:
+ using Layout = FossilizedValLayout;
+ static const FossilizedValKind kKind = FossilizedValKind::Bool;
+
+ bool get() const { return _value != 0; }
+
+ operator bool() const { return get(); }
+
+ static bool isMatchingKind(FossilizedValKind kind) { return kind == kKind; }
+
+private:
+ uint8_t _value;
+};
+
+static_assert(sizeof(Fossilized) == 1);
+
+//
+// Some simple types can be fossilized as one of the
+// scalar types above, with explicit casts between
+// the "live" type and the "fossilized" type.
+//
+// A common example of this is `enum` types.
+//
+
+template
+struct FossilizedViaCastVal
+{
+public:
+ LiveType get() const { return LiveType(_value.get()); }
+
+ operator LiveType() const { return get(); }
+
+
+private:
+ Fossilized _value;
+};
+
+//
+// By default we assume that an `enum` type should be fossilized
+// as a signed 32-bit integer.
+//
+#define SLANG_DECLARE_FOSSILIZED_ENUM(TYPE) \
+ template<> \
+ struct Fossilized_ : FossilizedViaCastVal \
+ { \
+ };
+
+//
+// For many of the other kinds of types that get fossilized,
+// the in-memory encoding will typically be as a (relative)
+// pointer to the actual data.
+//
+// Here we distinguish between the *value* type (e.g.,
+// `FossilizedString`) that typically gets stored as the
+// field of a record/tuple/whatever, and the *object* type
+// that the value type is a (relative) pointer to (e.g.,
+// `FossilizedStringObj`).
+//
+
+struct FossilizedStringObj
+{
+public:
+ Size getSize() const;
+ UnownedTerminatedStringSlice get() const;
+
+ operator UnownedTerminatedStringSlice() const { return get(); }
+
+ using Layout = FossilizedValLayout;
+
+ static bool isMatchingKind(FossilizedValKind kind)
+ {
+ return kind == FossilizedValKind::StringObj;
+ }
+
+private:
+ // Before the `this` address, there is a `FossilUInt`
+ // with the size of the string in bytes.
+ //
+ // At the `this` address there is a nul-terminated
+ // sequence of `getSize() + 1` bytes.
+};
+
+//
+// The array and dictionary types are handled largely
+// the same as strings, with the added detail that the
+// object type is split into a base type without the
+// template parameters, and a subtype that has those
+// parameters. The base type enables navigating of
+// these containers dynamically, based on layout.
+//
+
+struct FossilizedContainerLayout
+{
+ FossilizedValKind kind;
+ FossilizedPtr elementLayout;
+ FossilUInt elementStride;
+};
+
+struct FossilizedContainerObjBase
+{
+public:
+ using Layout = FossilizedContainerLayout;
+
+ Count getElementCount() const;
+
+ void const* getBuffer() const { return this; }
+
+ static bool isMatchingKind(FossilizedValKind kind)
+ {
+ switch (kind)
+ {
+ default:
+ return false;
+
+ case FossilizedValKind::ArrayObj:
+ case FossilizedValKind::DictionaryObj:
+ return true;
+ }
+ }
+
+private:
+ // Before the `this` address, there is a `FossilUInt`
+ // with the number of elements.
+ //
+ // At the `this` address there is a sequence of
+ // `getCount()` elements. The layout of those elements
+ // cannot be determined without having a `FossilizedContainerLayout`
+ // for this container.
+};
+
+template
+struct FossilizedContainerObj : FossilizedContainerObjBase
+{
+public:
+};
+
+struct FossilizedArrayObjBase : FossilizedContainerObjBase
+{
+public:
+ static bool isMatchingKind(FossilizedValKind kind)
+ {
+ return kind == FossilizedValKind::ArrayObj;
+ }
+};
+
+template
+struct FossilizedArrayObj : FossilizedArrayObjBase
+{
+};
+
+//
+// While we defined the core `FossilizedPtr` type above, there is
+// some subtlety involved in defining the way that a C++ pointer
+// type like `T*` maps to its fossilized representation via
+// `Fossilized`. The reason for this is that the binary layout
+// of fossilized data avoids storing redundant pointers-to-pointers,
+// so because a `Dictionary` would already be stored
+// via an indirection in the binary layout, a pointer type
+// `Dictionary *` would be stored with the exact same
+// binary layout.
+//
+// When computing what `Fossilized` is, the result will be
+// `FossilizedPtr< FossilizedPtrTarget >`. The `FossilizedPtrTarget`
+// template uses a set of helpers defined in a `details` namespace
+// to compute the correct target type.
+//
+
+namespace details
+{
+//
+// By default, a `Fossilized` will just be a `FossilizedPtr>`.
+//
+template
+T fossilizedPtrTargetType(T*, void*);
+} // namespace details
+
+template
+using FossilizedPtrTarget = decltype(details::fossilizedPtrTargetType(
+ std::declval*>(),
+ std::declval*>()));
+
+
+template
+struct FossilizedTypeTraits
+{
+ using FossilizedType = FossilizedPtr>;
+};
+
+template
+struct FossilizedTypeTraits>
+{
+ using FossilizedType = FossilizedPtr>;
+};
+
+
+//
+// An optional value is effectively just a pointer, with
+// the null case being used to represent the absence of
+// a value.
+//
+
+struct FossilizedPtrLikeLayout
+{
+ // Note: we aren't using inheritance in the definitions
+ // of these types, because per the letter of the law in
+ // C++, a type is only "standard layout" when there is
+ // only a single type in the inheritance hierarchy that
+ // has (non-static) data members.
+
+ FossilizedValKind kind;
+ FossilizedPtr elementLayout;
+};
+
+struct FossilizedOptionalObjBase
+{
+public:
+ void* getValue() { return this; }
+
+ void const* getValue() const { return this; }
+
+ using Layout = FossilizedPtrLikeLayout;
+
+ static bool isMatchingKind(FossilizedValKind kind)
+ {
+ return kind == FossilizedValKind::OptionalObj;
+ }
+
+private:
+ // An absent optional is encoded as a null pointer
+ // (so `this` would be null), while a present value
+ // is encoded as a pointer to that value. Thus the
+ // held value is at the same address as `this`.
+};
+
+template
+struct FossilizedOptionalObj : FossilizedOptionalObjBase
+{
+ T* getValue() { return this; }
+
+ T const* getValue() const { return this; }
+};
+
+template
+struct FossilizedOptional
+{
+public:
+ explicit operator bool() const { return _value.get() != nullptr; }
+ T const& operator*() const { return *_value.get(); }
+
+private:
+ FossilizedPtr _value;
+};
+
+template
+struct FossilizedTypeTraits>
+{
+ using FossilizedType = FossilizedOptional>;
+};
+
+static_assert(sizeof(Fossilized>) == sizeof(FossilUInt));
+
+//
+// With all of the various `Fossilized*Obj` cases defined above,
+// we can now define the more direct versions of things that
+// apply in the common case. For example, `Fossilized`
+// simply maps to the `FossilizedString` type, and the parallels
+// are similar for arrays and dictionaries.
+//
+
+struct FossilizedString
+{
+public:
+ Size getSize() const { return _obj ? _obj->getSize() : 0; }
+
+ UnownedTerminatedStringSlice get() const
+ {
+ return _obj ? _obj->get() : UnownedTerminatedStringSlice();
+ }
+
+ operator UnownedTerminatedStringSlice() const { return get(); }
+
+private:
+ FossilizedPtr _obj;
+};
+
+inline int compare(FossilizedString const& lhs, UnownedStringSlice const& rhs)
+{
+ return compare(lhs.get(), rhs);
+}
+
+inline bool operator==(FossilizedString const& left, UnownedStringSlice const& right)
+{
+ return left.get() == right;
+}
+
+inline bool operator!=(FossilizedString const& left, UnownedStringSlice const& right)
+{
+ return left.get() != right;
+}
+
+inline bool operator==(FossilizedStringObj const& left, UnownedStringSlice const& right)
+{
+ return left.get() == right;
+}
+
+inline bool operator!=(FossilizedStringObj const& left, UnownedStringSlice const& right)
+{
+ return left.get() != right;
+}
+
+#define SLANG_DECLARE_FOSSILIZED_TYPE(LIVE, FOSSILIZED) \
+ template<> \
+ struct FossilizedTypeTraits \
+ { \
+ using FossilizedType = FOSSILIZED; \
+ }
+
+SLANG_DECLARE_FOSSILIZED_TYPE(String, FossilizedString);
+SLANG_DECLARE_FOSSILIZED_TYPE(UnownedStringSlice, FossilizedString);
+SLANG_DECLARE_FOSSILIZED_TYPE(UnownedTerminatedStringSlice, FossilizedString);
+
+static_assert(std::is_same_v, FossilizedString>);
+static_assert(sizeof(Fossilized) == sizeof(FossilUInt));
+
+template
+struct FossilizedContainer
+{
+public:
+ Count getElementCount() const
+ {
+ if (!_obj)
+ return 0;
+ return _obj->getElementCount();
+ }
+ T const* getBuffer() const
+ {
+ if (!_obj)
+ return nullptr;
+ return (T const*)_obj.get()->getBuffer();
+ }
+
+ T const* begin() const { return getBuffer(); }
+ T const* end() const { return getBuffer() + getElementCount(); }
+
+private:
+ FossilizedPtr> _obj;
+};
+
+template
+struct FossilizedArray : FossilizedContainer
+{
+public:
+ T const& operator[](Index index) const
+ {
+ SLANG_ASSERT(index >= 0 && index < this->getElementCount());
+ return this->getBuffer()[index];
+ }
+};
+
+template
+struct FossilizedTypeTraits>
+{
+ using FossilizedType = FossilizedArray>;
+};
+
+template
+struct FossilizedTypeTraits>
+{
+ using FossilizedType = FossilizedArray>;
+};
+
+template
+struct FossilizedTypeTraits
+{
+ using FossilizedType = FossilizedArray>;
+};
+
+static_assert(sizeof(Fossilized>) == sizeof(FossilUInt));
+
+template
+struct FossilizedKeyValuePair
+{
+ using Layout = FossilizedRecordLayout;
+ K key;
+ V value;
+};
+
+template
+struct FossilizedTypeTraits>
+{
+ using FossilizedType = FossilizedKeyValuePair, Fossilized>;
+};
+
+template
+struct FossilizedTypeTraits>
+{
+ using FossilizedType = FossilizedKeyValuePair, Fossilized>;
+};
+
+//
+// In terms of the encoding, a fossilized dictionary
+// is really just an array of key-value pairs, but
+// we keep the types distinct to help with clarity.
+//
+
+struct FossilizedDictionaryObjBase : FossilizedContainerObjBase
+{
+public:
+ static bool isMatchingKind(FossilizedValKind kind)
+ {
+ return kind == FossilizedValKind::DictionaryObj;
+ }
+};
+
+template
+struct FossilizedDictionaryObj : FossilizedDictionaryObjBase
+{
+};
+
+template
+struct FossilizedDictionary : FossilizedContainer>
+{
+public:
+ using Entry = FossilizedKeyValuePair;
+};
+
+template
+struct FossilizedTypeTraits>
+{
+ using FossilizedType = FossilizedDictionary, Fossilized>;
+};
+
+template
+struct FossilizedTypeTraits>
+{
+ using FossilizedType = FossilizedDictionary, Fossilized>;
+};
+
+static_assert(sizeof(Fossilized>) == sizeof(FossilUInt));
+
+//
+// A record (struct or tuple) is stored simply as a sequence of field
+// values, and its layout gives the total number of fields as well as
+// the offset and layout of each.
+//
+
+struct FossilizedRecordElementLayout
+{
+ FossilizedPtr layout;
+ FossilUInt offset;
+};
+
+struct FossilizedRecordLayout
+{
+ FossilizedValKind kind;
+ FossilUInt fieldCount;
+
+ // FossilizedRecordElementLayout elements[];
+
+ FossilizedRecordElementLayout* getField(Index index) const;
+};
+
+/// Stand-in for a fossilized record of unknown type.
+///
+/// Note that user-defined fossilized types should *not* try
+/// to inherit from `FossilizedRecordVal`, as doing so can
+/// end up breaking the correlation between the binary layout
+/// of fossilized data and the matching C++ declarations.
+///
+struct FossilizedRecordVal
+{
+public:
+ using Layout = FossilizedRecordLayout;
+
+ static bool isMatchingKind(FossilizedValKind kind)
+ {
+ switch (kind)
+ {
+ default:
+ return false;
+
+ case FossilizedValKind::Struct:
+ case FossilizedValKind::Tuple:
+ return true;
+ }
+ }
+};
+
+//
+// A *variant* is a value that can conceptually hold data of any type/layout,
+// and stores a pointer to layout information so that the data it holds
+// can be navigated dynamically.
+//
-template
-using FossilizedPtr = RelativePtr32;
+struct FossilizedVariantObj
+{
+public:
+ using Layout = FossilizedValLayout;
+ static const FossilizedValKind kKind = FossilizedValKind::VariantObj;
+
+ FossilizedValLayout* getContentLayout() const;
+
+ void* getContentDataPtr() { return this; }
+ void const* getContentDataPtr() const { return this; }
+
+ static bool isMatchingKind(FossilizedValKind kind)
+ {
+ return kind == FossilizedValKind::VariantObj;
+ }
+
+private:
+ // Before the `this` address, there is a `FossilizedPtr`
+ // with the layout of the content.
+ //
+ // The content itself starts at the `this` address, with its
+ // layout determined by `getContentLayout()`.
+};
+
+struct FossilizedVariant
+{
+public:
+private:
+ FossilizedPtr _obj;
+};
+
+static_assert(sizeof(FossilizedVariant) == sizeof(FossilUInt));
-// Various other parts of the format need to store offsets or counts,
-// and for consistency we will store them with the same number of
-// bits as the relative pointers already used in the format.
//
-// To make it easier for us to (potentially) change the relative
-// pointer size down the line, we define type aliases for the
-// general-purpose integer types that will be used in fossilized data.
+// Now that all of the relevant types for fossilized data have been defined,
+// we can circle back to define the specializations of `FossilizedPtrTargetType`
+// for the types that need it.
+//
+
+namespace details
+{
+template
+FossilizedStringObj fossilizedPtrTargetType(X*, FossilizedString*);
+
+template
+FossilizedVariantObj fossilizedPtrTargetType(X*, FossilizedVariant*);
-using FossilInt = FossilizedPtr::Offset;
-using FossilUInt = FossilizedPtr::UOffset;
+template
+FossilizedArrayObj fossilizedPtrTargetType(X*, FossilizedArray*);
+
+template
+FossilizedDictionaryObj fossilizedPtrTargetType(X*, FossilizedDictionary*);
+} // namespace details
//
-// The fossil format supports data that is *self-describing*.
+// In addition to being able to expose a statically-known
+// layout through `Fossilized`, the fossil format also
+// allows data to be *self-describing*, by carying its layout
+// with it.
//
// A `FossilizedValLayout` describes the in-memory layout of a fossilized
// value. Given a `FossilizedValLayout` and a pointer to the data
// for a particular value, it is possible to inspect the structure
// of the fossilized data.
//
-// If all you have is a `FossilizedVal*`, then there is no way to access
-// its contents without assuming it is of some particular type and casting
-// it.
+// If all you have is a `void*` to a fossilzied value, then there is no way
+// to access its contents without assuming it is of some particular type and
+// casting it.
//
// A `FossilizedVariantObj` is a fossilized value that is self-describing;
// it stores a (relative) pointer to a layout, which can be used to inspect
// its own data/state.
//
-struct FossilizedVal;
struct FossilizedValLayout;
+struct FossilizedPtrLikeLayout;
+struct FossilizedContainerLayout;
+struct FossilizedRecordLayout;
struct FossilizedVariantObj;
-/// Kinds of values that can appear in fossilized data.
-enum class FossilizedValKind : FossilUInt
-{
- Bool,
- Int8,
- Int16,
- Int32,
- Int64,
- UInt8,
- UInt16,
- UInt32,
- UInt64,
- Float32,
- Float64,
- String,
- Array,
- Optional,
- Dictionary,
- Tuple,
- Struct,
- Ptr,
- Variant,
-};
-
/// Layout information about a fossilized value in memory.
///
///
@@ -97,339 +789,336 @@ struct FossilizedValLayout
FossilizedValKind kind;
};
-struct FossilizedPtrLikeLayout
-{
- // Note: we aren't using inheritance in the definitions
- // of these types, because per the letter of the law in
- // C++, a type is only "standard layout" when there is
- // only a single type in the inheritance hierarchy that
- // has (non-static) data members.
-
- FossilizedValKind kind;
- FossilizedPtr elementLayout;
-};
-
-struct FossilizedContainerLayout
-{
- FossilizedValKind kind;
- FossilizedPtr elementLayout;
- FossilUInt elementStride;
-};
-
-struct FossilizedRecordElementLayout
-{
- FossilizedPtr layout;
- FossilUInt offset;
-};
-
-struct FossilizedRecordLayout
+namespace Fossil
{
- FossilizedValKind kind;
- FossilUInt fieldCount;
-
- // FossilizedRecordElementLayout elements[];
-
- FossilizedRecordElementLayout* getField(Index index) const;
-};
-
-/// A reference to a fossilized value in memory (of type T), and its layout.
+/// A reference to a fossilized value in memory, along with layout information.
///
-template
-struct FossilizedValRef_
+template
+struct ValRefBase
{
public:
using Val = T;
- using Layout = typename T::Layout;
+ using Layout = L;
/// Construct a null reference.
///
- FossilizedValRef_() {}
+ ValRefBase() {}
/// Construct a reference to the given `data`, assuming it has the given `layout`.
///
- FossilizedValRef_(T* data, Layout* layout)
+ ValRefBase(T* data, Layout const* layout)
: _data(data), _layout(layout)
{
}
- /// Get the kind of value being referenced.
+ /// Construct a copy of `ref`.
///
- /// This reference must not be null.
+ /// Only enabled if `U*` is convertible to `T*`.
///
- FossilizedValKind getKind()
+ template
+ ValRefBase(ValRefBase ref, std::enable_if_t, void>* = nullptr)
+ : _data(ref.getDataPtr()), _layout((Layout const*)ref.getLayout())
{
- SLANG_ASSERT(getLayout());
- return getLayout()->kind;
}
- /// Get the layout of the value being referenced.
+ /// Get a pointer to the value being referenced.
///
- Layout* getLayout() { return _layout; }
+ T* getDataPtr() const { return _data; }
- /// Get a pointer to the value being referenced.
+ /// Get a reference to the value being referenced.
+ ///
+ /// This accessor is disabled in the case where `T` is `void`.
///
- T* getData() { return _data; }
+ template
+ std::enable_if_t, T>& getDataRef() const
+ {
+ return *_data;
+ }
- operator T*() const { return _data; }
+ /// Get the layout of the value being referenced.
+ ///
+ Layout const* getLayout() const { return _layout; }
- T* operator->() { return _data; }
+ /// Get the kind of value being referenced.
+ ///
+ /// This reference must not be null.
+ ///
+ FossilizedValKind getKind() const
+ {
+ SLANG_ASSERT(getLayout());
+ return getLayout()->kind;
+ }
-private:
+protected:
T* _data = nullptr;
- Layout* _layout = nullptr;
+ Layout const* _layout = nullptr;
};
-using FossilizedValRef = FossilizedValRef_;
+/// A reference to a fossilized value in memory, along with layout information.
+///
+template
+struct ValRef : ValRefBase
+{
+ using ValRefBase::ValRefBase;
+};
-/// A fossilized value in memory.
+/// Specialization of `ValRef` for the case where `T` is `void`.
///
-/// There isn't a lot that can be done with a bare pointer to
-/// a `FossilizedVal`. This type is mostly declared to allow
-/// us to make it explicit when a pointer points to a fossilized
-/// value (even if we don't know anything about its layout).
+template<>
+struct ValRef : ValRefBase
+{
+ using ValRefBase::ValRefBase;
+};
+
+/// A pointer to a fossilized value in memory, along with layout information.
///
-struct FossilizedVal
+template
+struct ValPtr
{
public:
- using Kind = FossilizedValKind;
- using Layout = FossilizedValLayout;
+ using TargetVal = T;
+ using TargetLayout = typename ValRef::Layout;
+
+ /// Construct a null pointer.
+ ///
+ ValPtr() {}
+ ValPtr(std::nullptr_t) {}
- /// Determine if a value with the given `kind` should be allowed to cast to this type.
- static bool _isMatchingKind(Kind kind)
+ /// Construct a pointer to the given `data`, assuming it has the given `layout`.
+ ///
+ ValPtr(T* data, TargetLayout const* layout)
+ : _ref(data, layout)
{
- SLANG_UNUSED(kind);
- return true;
}
-protected:
- FossilizedVal() = default;
- FossilizedVal(FossilizedVal const&) = default;
- FossilizedVal(FossilizedVal&&) = default;
- ~FossilizedVal() = default;
-};
+ /// Construct a pointer to the value referenced by `ref`.
+ ///
+ /// This constructor is basically equivalent to the address-of operator `&`.
+ /// We define it as a constructor as a slightly more preferable alternative
+ /// to overloading prefix `operator&` (which is almost always a Bad Idea)
+ ///
+ explicit ValPtr(ValRef ref)
+ : _ref(ref)
+ {
+ }
-template
-struct FossilizedSimpleVal : FossilizedVal
-{
-public:
- T getValue() const { return _value; }
+ /// Construct a copy of `ptr`.
+ ///
+ /// Only enabled if `U*` is convertible to `T*`.
+ ///
+ template
+ ValPtr(ValPtr ptr, std::enable_if_t, void>* = nullptr)
+ : _ref(*ptr)
+ {
+ }
+
+ /// Get a pointer to the value being referenced.
+ ///
+ T* getDataPtr() const { return _ref.getDataPtr(); }
+
+ /// Get the layout of the value being referenced.
+ ///
+ TargetLayout* getLayout() const { return _ref.getLayout(); }
+
+ T* get() const { return _ref.getDataPtr(); }
+ operator T*() const { return get(); }
+
+ /// Deference this `ValPtr` to get a `ValRef`.
+ ///
+ ValRef operator*() const { return _ref; }
- /// Determine if a value with the given `kind` should be allowed to cast to this type.
- static bool _isMatchingKind(Kind kind) { return kind == kKind; }
+ /// Deference this `ValPtr` for member access.
+ ///
+ /// Note that an overloaded `operator->` must return either
+ /// a pointer or a type that itself overloads `operator->`.
+ /// Because `ValRef` is not functionally a "smart pointer"
+ /// to a `T`, the logical behavior here is that we want
+ /// `someValPtr->foo` to be equvialent to `someValRef.foo`,
+ /// where `someValRef` is a reference to the same value
+ /// that `someValPtr` points to. The correct way to get
+ /// that behavior is for the `operator->` on `ValPtr`
+ /// to return a pointer to a `ValRef`.
+ ///
+ ValRef const* operator->() const { return &_ref; }
private:
- T _value;
+ ValRef _ref;
};
-using FossilizedInt8Val = FossilizedSimpleVal;
-using FossilizedInt16Val = FossilizedSimpleVal;
-using FossilizedInt32Val = FossilizedSimpleVal;
-using FossilizedInt64Val = FossilizedSimpleVal;
+/// Get a `ValPtr` pointing to the same value as the given `ref`.
+///
+template
+inline ValPtr getAddress(ValRef ref)
+{
+ return ValPtr(ref);
+}
-using FossilizedUInt8Val = FossilizedSimpleVal;
-using FossilizedUInt16Val = FossilizedSimpleVal;
-using FossilizedUInt32Val = FossilizedSimpleVal;
-using FossilizedUInt64Val = FossilizedSimpleVal;
+using AnyValRef = ValRef;
+using AnyValPtr = ValPtr;
-using FossilizedFloat32Val = FossilizedSimpleVal;
-using FossilizedFloat64Val = FossilizedSimpleVal;
+//
+// In order to make `ValRef` more usable in contexts where we want
+// to make use of the knowledge that it refers to a `T`, we define
+// various specializations of `ValRef` for the specific types that
+// are relevant for decoding serialized data.
+//
+// Note that we do not need to define any specializations of
+// `ValPtr`, because that is ultimately just a wrapper around
+// `ValRef`.
+//
-struct FossilizedBoolVal : FossilizedVal
+template<>
+struct ValRef : ValRefBase
{
public:
- bool getValue() const { return _value != 0; }
+ using ValRefBase::ValRefBase;
- /// Determine if a value with the given `kind` should be allowed to cast to this type.
- static bool _isMatchingKind(Kind kind) { return kind == Kind::Bool; }
+ Size getSize() const { return getDataPtr()->getSize(); }
+ UnownedTerminatedStringSlice get() const { return getDataPtr()->get(); }
-private:
- uint8_t _value;
+ operator UnownedTerminatedStringSlice() const { return get(); }
};
-struct FossilizedPtrVal : FossilizedVal
+
+template<>
+struct ValRef : ValRefBase
{
public:
- using Layout = FossilizedPtrLikeLayout;
-
- FossilizedVal* getTargetData() const { return _value.get(); }
+ using ValRefBase::ValRefBase;
- /// Determine if a value with the given `kind` should be allowed to cast to this type.
- static bool _isMatchingKind(Kind kind) { return kind == Kind::Ptr; }
+ Count getElementCount() const
+ {
+ auto data = this->getDataPtr();
+ if (!data)
+ return 0;
+ return data->getElementCount();
+ }
-private:
- FossilizedPtr _value;
+ AnyValRef getElement(Index index) const;
};
-struct FossilizedRecordVal : FossilizedVal
+template<>
+struct ValRef : ValRefBase
{
public:
- using Layout = FossilizedRecordLayout;
+ using ValRefBase::ValRefBase;
- /// Determine if a value with the given `kind` should be allowed to cast to this type.
- static bool _isMatchingKind(Kind kind)
+ Count getElementCount() const
{
- switch (kind)
- {
- default:
- return false;
-
- case Kind::Struct:
- case Kind::Tuple:
- return true;
- }
+ auto data = this->getDataPtr();
+ if (!data)
+ return 0;
+ return data->getElementCount();
}
+
+ AnyValRef getElement(Index index) const;
};
-//
-// Some of the following subtypes of `FossilizedVal` are
-// named as `Fossilized*Obj` rather than `Fossilized*Val`,
-// to indicate that they will only ever be located on the
-// other side of a pointer indirection.
-//
-// E.g., a field of a fossilized struct value should never
-// have a layout claiming it to be of kind `String`; instead
-// it should show as a field of kind `Ptr`, where the
-// pointed-to type is `String`. The same goes for `Optional`,
-// `Array`, and `Dictionary`.
-//
-// This distinction only matters when dealing with things like
-// an *optional* string, because instead of an in-memory
-// layout like `Ptr -> Optional -> Ptr -> String`, the fossilized
-// data will simply store `Ptr -> Optional -> String`.
-//
-struct FossilizedStringObj : FossilizedVal
+template<>
+struct ValRef : ValRefBase
{
public:
- Size getSize() const;
- UnownedTerminatedStringSlice getValue() const;
+ using ValRefBase::ValRefBase;
- /// Determine if a value with the given `kind` should be allowed to cast to this type.
- static bool _isMatchingKind(Kind kind) { return kind == Kind::String; }
+ Count getElementCount() const
+ {
+ auto data = this->getDataPtr();
+ if (!data)
+ return 0;
+ return data->getElementCount();
+ }
-private:
- // Before the `this` address, there is a `FossilUInt`
- // with the size of the string in bytes.
- //
- // At the `this` address there is a nul-terminated
- // serquence of `getSize() + 1` bytes.
+ AnyValRef getElement(Index index) const;
};
-
-struct FossilizedOptionalObj : FossilizedVal
+template<>
+struct ValRef : ValRefBase
{
public:
- using Layout = FossilizedPtrLikeLayout;
-
- /// Determine if a value with the given `kind` should be allowed to cast to this type.
- static bool _isMatchingKind(Kind kind) { return kind == Kind::Optional; }
-
- FossilizedVal* getValue() { return this; }
+ using ValRefBase::ValRefBase;
- FossilizedVal const* getValue() const { return this; }
+ bool hasValue() const { return this->getDataPtr() != nullptr; }
-private:
- // An absent optional is encoded as a null pointer
- // (so `this` would be null), while a present value
- // is encoded as a pointer to that value. Thus the
- // held value is at the same address as `this`.
+ AnyValRef getValue() const
+ {
+ SLANG_ASSERT(hasValue());
+ return AnyValRef(this->getDataPtr(), this->getLayout()->elementLayout.get());
+ }
};
-struct FossilizedContainerObj : FossilizedVal
+template<>
+struct ValRef : ValRefBase
{
public:
- using Layout = FossilizedContainerLayout;
+ using ValRefBase::ValRefBase;
- Count getElementCount() const;
-
- /// Determine if a value with the given `kind` should be allowed to cast to this type.
- static bool _isMatchingKind(Kind kind)
- {
- switch (kind)
- {
- default:
- return false;
-
- case Kind::Array:
- case Kind::Dictionary:
- return true;
- }
- }
+ Count getFieldCount() const { return getLayout()->fieldCount; }
-private:
- // Before the `this` address, there is a `FossilUInt`
- // with the number of elements.
- //
- // At the `this` address there is a sequence of
- // `getCount()` elements. The layout of those elements
- // cannot be determined without having a `FossilizedContainerLayout`
- // for this container.
+ AnyValRef getField(Index index) const;
};
-struct FossilizedVariantObj : FossilizedVal
+template
+struct ValRef> : ValRefBase>
{
public:
- FossilizedValLayout* getContentLayout() const;
-
+ using ValRefBase>::ValRefBase;
- FossilizedVal* getContentData() { return this; }
- FossilizedVal const* getContentData() const { return this; }
+ ValRef getTargetValRef() const
+ {
+ auto ptrPtr = this->getDataPtr();
+ return ValRef(*ptrPtr, this->getLayout()->elementLayout.get());
+ }
- static bool _isMatchingKind(Kind kind) { return kind == Kind::Variant; }
+ ValPtr getTargetValPtr() const { return ValPtr(getTargetValRef()); }
-private:
- // Before the `this` address, there is a `FossilizedPtr`
- // with the layout of the content.
- //
- // The content itself starts at the `this` address, with its
- // layout determined by `getContentLayout()`.
+ // ValRef operator*() const;
};
-/// Dynamic cast of a reference to a fossilized value.
+//
+// We support both static and dynamic casting of `ValPtr`s
+// to fossilized data. In the dynamic case, the layout
+// information associated with the pointer is used to
+// determine if the cast is allowed.
+//
+
+/// Statically cast a pointer to a fossilized value.
///
-template
-FossilizedValRef_ as(FossilizedValRef_ valRef)
+template
+ValPtr cast(AnyValPtr valPtr)
{
- if (!valRef || !T::_isMatchingKind(valRef.getKind()))
- return FossilizedValRef_();
-
- return FossilizedValRef_(
- static_cast