diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2020-06-24 13:56:06 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-24 13:56:06 -0400 |
| commit | ae41db80aa95ee7243d91a3ae4f56e6deb17f7f4 (patch) | |
| tree | 444c0d10680b8ab75e82225155fcd3e8224204f0 | |
| parent | b595dd0cd984bcb4db980693363798dd2b490da4 (diff) | |
AST Serialization writing (#1407)
* Try to fix problem with C++ extractor concating tokens producing an erroneous result.
* Improve naming/comments around C++ extractor fix.
* Another small improvement around space concating when outputing token list.
* Handle some more special cases for consecutive tokens for C++ extractor concat of tokens.
* WIP AST serialization.
* Comment out so compile works.
* More work on AST serialization.
* WIP AST serialize.
* WIP AST Serialization - handling more types.
* WIP: Compiles but not all types are converted, as not all List element types are handled.
* Compiles with array types.
* Finish off AST serialization of remaining types.
* Remove ComputedLayoutModifier and TupleVarModifier.
* Add fields to ASTSerialClass type.
* Construct AST type layout.
* AST Serialization working for writing to ASTSerialWriter.
* Removed call to ASTSerialization::selfTest in session creation.
* Fixes for gcc.
* Diagnostics handling - better handling of dashify.
* Improve comment around DiagnosticLookup.
* Updated VS project.
| -rw-r--r-- | source/slang/slang-ast-base.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-ast-modifier.h | 17 | ||||
| -rw-r--r-- | source/slang/slang-ast-reflect.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ast-serialize.cpp | 1289 | ||||
| -rw-r--r-- | source/slang/slang-ast-serialize.h | 254 | ||||
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 16 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 174 | ||||
| -rw-r--r-- | source/slang/slang-diagnostics.cpp | 141 | ||||
| -rw-r--r-- | source/slang/slang-diagnostics.h | 1 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 13 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 2 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 6 |
12 files changed, 1790 insertions, 128 deletions
diff --git a/source/slang/slang-ast-base.h b/source/slang/slang-ast-base.h index 27ac8a437..847dada03 100644 --- a/source/slang/slang-ast-base.h +++ b/source/slang/slang-ast-base.h @@ -275,7 +275,7 @@ class Modifier : public SyntaxNode Modifier* next = nullptr; // The keyword that was used to introduce t that was used to name this modifier. - Name* name; + Name* name = nullptr; Name* getName() { return name; } NameLoc getNameAndLoc() { return NameLoc(name, loc); } diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h index 30ef1f921..1cbfab7c3 100644 --- a/source/slang/slang-ast-modifier.h +++ b/source/slang/slang-ast-modifier.h @@ -814,23 +814,6 @@ class HLSLTriangleAdjModifier : public HLSLGeometryShaderInputPrimitiveTypeModif SLANG_CLASS(HLSLTriangleAdjModifier) }; - -// A modifier to be attached to syntax after we've computed layout -class ComputedLayoutModifier : public Modifier -{ - SLANG_CLASS(ComputedLayoutModifier) - - RefPtr<Layout> layout; -}; - - -class TupleVarModifier : public Modifier -{ - SLANG_CLASS(TupleVarModifier) - -// TupleFieldModifier* tupleField = nullptr; -}; - // A modifier to indicate that a constructor/initializer can be used // to perform implicit type conversion, and to specify the cost of // the conversion, if applied. diff --git a/source/slang/slang-ast-reflect.h b/source/slang/slang-ast-reflect.h index 8df8dbc38..3b055cc87 100644 --- a/source/slang/slang-ast-reflect.h +++ b/source/slang/slang-ast-reflect.h @@ -20,7 +20,8 @@ SLANG_FORCE_INLINE static bool isDerivedFrom(ASTNodeType type) { return int(type) >= int(kType) && int(type) <= int(ASTNodeType::LAST); } \ SLANG_CLASS_REFLECT_SUPER_##TYPE(SUPER) \ friend class ASTBuilder; \ - friend struct ASTConstructAccess; + friend struct ASTConstructAccess; \ + friend struct ASTFieldAccess; // Macro definitions - use the SLANG_ASTNode_ definitions to invoke the IMPL to produce the code // injected into AST classes diff --git a/source/slang/slang-ast-serialize.cpp b/source/slang/slang-ast-serialize.cpp new file mode 100644 index 000000000..411e7a7ad --- /dev/null +++ b/source/slang/slang-ast-serialize.cpp @@ -0,0 +1,1289 @@ +// slang-ast-serialize.cpp +#include "slang-ast-serialize.h" + +#include "slang-ast-generated.h" +#include "slang-ast-generated-macro.h" + +#include "slang-compiler.h" +#include "slang-type-layout.h" + +#include "slang-ast-support-types.h" + +#include "../core/slang-byte-encode-util.h" + +namespace Slang { + + +// Things stored as references: +// +// NodeBase derived types +// Array +// +// RefObject derived types: +// +// Breadcrumb +// StringRepresentation +// Scope + + + +// Helpers to convert fields treated as values + +class ASTSerialReader; +class ASTSerialWriter; + +template <typename NATIVE_TYPE, typename SERIAL_TYPE> +static void _toSerialValue(ASTSerialWriter* writer, const NATIVE_TYPE& src, SERIAL_TYPE& dst) +{ + ASTSerialTypeInfo<NATIVE_TYPE>::toSerial(writer, &src, &dst); +} + +template <typename SERIAL_TYPE, typename NATIVE_TYPE> +static void _toNativeValue(ASTSerialReader* reader, const SERIAL_TYPE& src, NATIVE_TYPE& dst) +{ + ASTSerialTypeInfo<NATIVE_TYPE>::toNative(reader, &src, &dst); +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Serial <-> Native conversion !!!!!!!!!!!!!!!!!!!!!!!! + + +// We need to have a way to map between the two. +// If no mapping is needed, (just a copy), then we don't bother with the functions +template <typename T> +struct ASTSerialBasicTypeInfo +{ + typedef T NativeType; + typedef T SerialType; + + // We want the alignment to be the same as the size of the type for basic types + // NOTE! Might be different from SLANG_ALIGN_OF(SerialType) + enum { SerialAlignment = sizeof(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { SLANG_UNUSED(writer); *(T*)serial = *(const T*)native; } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); *(T*)native = *(const T*)serial; } + + static const ASTSerialType* getType() + { + static const ASTSerialType type = { sizeof(SerialType), uint8_t(SerialAlignment), &toSerial, &toNative }; + return &type; + } +}; + +template <typename NATIVE_T, typename SERIAL_T> +struct ASTSerialConvertTypeInfo +{ + typedef NATIVE_T NativeType; + typedef SERIAL_T SerialType; + + enum { SerialAlignment = ASTSerialBasicTypeInfo<SERIAL_T>::SerialAlignment }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { SLANG_UNUSED(writer); *(SERIAL_T*)serial = SERIAL_T(*(const NATIVE_T*)native); } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); *(NATIVE_T*)native = NATIVE_T(*(const SERIAL_T*)serial); } +}; + +template <typename T> +struct ASTSerialIdentityTypeInfo +{ + typedef T NativeType; + typedef T SerialType; + + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { SLANG_UNUSED(writer); *(T*)serial = *(const T*)native; } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); *(T*)native = *(const T*)serial; } +}; + +// Don't need to convert the index type + +template <> +struct ASTSerialTypeInfo<ASTSerialIndex> : public ASTSerialIdentityTypeInfo<ASTSerialIndex> {}; + +// Implement for Basic Types + +template <> +struct ASTSerialTypeInfo<uint8_t> : public ASTSerialBasicTypeInfo<uint8_t> {}; +template <> +struct ASTSerialTypeInfo<uint16_t> : public ASTSerialBasicTypeInfo<uint16_t> {}; +template <> +struct ASTSerialTypeInfo<uint32_t> : public ASTSerialBasicTypeInfo<uint32_t> {}; +template <> +struct ASTSerialTypeInfo<uint64_t> : public ASTSerialBasicTypeInfo<uint64_t> {}; + +template <> +struct ASTSerialTypeInfo<int8_t> : public ASTSerialBasicTypeInfo<int8_t> {}; +template <> +struct ASTSerialTypeInfo<int16_t> : public ASTSerialBasicTypeInfo<int16_t> {}; +template <> +struct ASTSerialTypeInfo<int32_t> : public ASTSerialBasicTypeInfo<int32_t> {}; +template <> +struct ASTSerialTypeInfo<int64_t> : public ASTSerialBasicTypeInfo<int64_t> {}; + +template <> +struct ASTSerialTypeInfo<float> : public ASTSerialBasicTypeInfo<float> {}; +template <> +struct ASTSerialTypeInfo<double> : public ASTSerialBasicTypeInfo<double> {}; + +// SamplerStateFlavor + +template <> +struct ASTSerialTypeInfo<SamplerStateFlavor> : public ASTSerialConvertTypeInfo<SamplerStateFlavor, uint8_t> {}; + +// TextureFlavor + +template <> +struct ASTSerialTypeInfo<TextureFlavor> +{ + typedef TextureFlavor NativeType; + typedef uint16_t SerialType; + enum { SerialAlignment = sizeof(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { SLANG_UNUSED(writer); *(SerialType*)serial = ((const NativeType*)native)->flavor; } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); ((NativeType*)native)->flavor = *(const SerialType*)serial; } +}; + +// Fixed arrays + +template <typename T, size_t N> +struct ASTSerialTypeInfo<T[N]> +{ + typedef ASTSerialTypeInfo<T> ElementASTSerialType; + typedef typename ElementASTSerialType::SerialType SerialElementType; + + typedef T NativeType[N]; + typedef SerialElementType SerialType[N]; + + enum { SerialAlignment = ASTSerialTypeInfo<T>::SerialAlignment }; + + static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial) + { + SerialElementType* serial = (SerialElementType*)outSerial; + const T* native = (const T*)inNative; + for (Index i = 0; i < Index(N); ++i) + { + ElementASTSerialType::toSerial(writer, native + i, serial + i); + } + } + static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative) + { + const SerialElementType* serial = (const SerialElementType*)inSerial; + T* native = (T*)outNative; + for (Index i = 0; i < Index(N); ++i) + { + ElementASTSerialType::toNative(reader, serial + i, native + i); + } + } +}; + +// Special case bool - as we can't rely on size alignment +template <> +struct ASTSerialTypeInfo<bool> +{ + typedef bool NativeType; + typedef uint8_t SerialType; + + enum { SerialAlignment = sizeof(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial) + { + SLANG_UNUSED(writer); + *(SerialType*)outSerial = *(const NativeType*)inNative ? 1 : 0; + } + static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative) + { + SLANG_UNUSED(reader); + *(NativeType*)outNative = (*(const SerialType*)inSerial) != 0; + } +}; + + +// Pointer +// Could handle different pointer base types with some more template magic here, but instead went with Pointer type to keep +// things simpler. +template <typename T> +struct ASTSerialTypeInfo<T*> +{ + typedef T* NativeType; + typedef ASTSerialIndex SerialType; + + enum + { + SerialAlignment = SLANG_ALIGN_OF(SerialType) + }; + + static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial) + { + *(SerialType*)outSerial = writer->addPointer(*(T**)inNative); + } + static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative) + { + *(T**)outNative = reader->getPointer(*(const SerialType*)inSerial).dynamicCast<T>(); + } +}; + +struct ASTSerialDeclRefBaseTypeInfo +{ + typedef DeclRefBase NativeType; + struct SerialType + { + ASTSerialIndex substitutions; + ASTSerialIndex decl; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial) + { + SerialType& serial = *(SerialType*)outSerial; + const NativeType& native = *(const NativeType*)inNative; + + serial.decl = writer->addPointer(native.decl); + serial.substitutions = writer->addPointer(native.substitutions.substitutions); + } + static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative) + { + DeclRefBase& native = *(DeclRefBase*)(outNative); + const SerialType& serial = *(const SerialType*)inSerial; + + native.decl = reader->getPointer(serial.decl).dynamicCast<Decl>(); + native.substitutions.substitutions = reader->getPointer(serial.substitutions).dynamicCast<Substitutions>(); + } + static const ASTSerialType* getType() + { + static const ASTSerialType type = { sizeof(SerialType), uint8_t(SerialAlignment), &toSerial, &toNative }; + return &type; + } +}; + +template <typename T> +struct ASTSerialTypeInfo<DeclRef<T>> : public ASTSerialDeclRefBaseTypeInfo {}; + +// MatrixCoord can just go as is +template <> +struct ASTSerialTypeInfo<MatrixCoord> : ASTSerialIdentityTypeInfo<MatrixCoord> {}; + +// SourceLoc + +// Make the type exposed, so we can look for it if we want to remap. +template <> +struct ASTSerialTypeInfo<SourceLoc> +{ + typedef SourceLoc NativeType; + typedef ASTSerialSourceLoc SerialType; + enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialSourceLoc) }; + + static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial) + { + *(SerialType*)outSerial = writer->addSourceLoc(*(const NativeType*)inNative); + } + static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative) + { + *(NativeType*)outNative = reader->getSourceLoc(*(const SerialType*)inSerial); + } +}; + +// List +template <typename T, typename ALLOCATOR> +struct ASTSerialTypeInfo<List<T, ALLOCATOR>> +{ + typedef List<T, ALLOCATOR> NativeType; + typedef ASTSerialIndex SerialType; + + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + + dst = writer->addArray(src.getBuffer(), src.getCount()); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& dst = *(NativeType*)native; + auto& src = *(const SerialType*)serial; + + reader->getArray(src, dst); + } +}; + +// Dictionary +template <typename KEY, typename VALUE> +struct ASTSerialTypeInfo<Dictionary<KEY, VALUE>> +{ + typedef Dictionary<KEY, VALUE> NativeType; + struct SerialType + { + ASTSerialIndex keys; ///< Index an array + ASTSerialIndex values; ///< Index an array + }; + + typedef typename ASTSerialTypeInfo<KEY>::SerialType KeySerialType; + typedef typename ASTSerialTypeInfo<VALUE>::SerialType ValueSerialType; + + enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + + List<KeySerialType> keys; + List<ValueSerialType> values; + + Index count = Index(src.Count()); + keys.setCount(count); + values.setCount(count); + + Index i = 0; + for (const auto& pair : src) + { + ASTSerialTypeInfo<KEY>::toSerial(writer, &pair.Key, &keys[i]); + ASTSerialTypeInfo<VALUE>::toSerial(writer, &pair.Value, &values[i]); + i++; + } + + dst.keys = writer->addArray(keys.getBuffer(), count); + dst.values = writer->addArray(values.getBuffer(), count); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; + + // Clear it + dst = NativeType(); + + List<KEY> keys; + List<VALUE> values; + + reader->getArray(src.keys, keys); + reader->getArray(src.values, values); + + SLANG_ASSERT(keys.getCount() == values.getCount()); + + const Index count = keys.getCount(); + for (Index i = 0; i < count; ++i) + { + dst.Add(keys[i], values[i]); + } + } +}; + +// SyntaxClass<T> +template <typename T> +struct ASTSerialTypeInfo<SyntaxClass<T>> +{ + typedef SyntaxClass<T> NativeType; + typedef uint16_t SerialType; + + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + SLANG_UNUSED(writer); + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + dst = SerialType(src.classInfo->m_classId); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + SLANG_UNUSED(reader); + auto& src = *(const SerialType*)native; + auto& dst = *(NativeType*)serial; + dst.classInfo = ReflectClassInfo::getInfo(ASTNodeType(src)); + } +}; + +// Handle RefPtr - just convert into * to do the conversion +template <typename T> +struct ASTSerialTypeInfo<RefPtr<T>> +{ + typedef RefPtr<T> NativeType; + typedef typename ASTSerialTypeInfo<T*>::SerialType SerialType; + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + T* obj = src; + ASTSerialTypeInfo<T*>::toSerial(writer, &obj, serial); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + T* obj = nullptr; + ASTSerialTypeInfo<T*>::toNative(reader, serial, &obj); + *(NativeType*)native = obj; + } +}; + +// QualType + +template <> +struct ASTSerialTypeInfo<QualType> +{ + typedef QualType NativeType; + struct SerialType + { + ASTSerialIndex type; + uint8_t isLeftValue; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) }; + + static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial) + { + auto dst = (SerialType*)outSerial; + auto src = (const NativeType*)inNative; + dst->isLeftValue = src->isLeftValue ? 1 : 0; + dst->type = writer->addPointer(src->type); + } + static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative) + { + auto src = (const SerialType*)inSerial; + auto dst = (NativeType*)outNative; + dst->type = reader->getPointer(src->type).dynamicCast<Type>(); + dst->isLeftValue = src->isLeftValue != 0; + } +}; + + +// LookupResult::Breadcrumb +template <> +struct ASTSerialTypeInfo<LookupResultItem::Breadcrumb> +{ + typedef LookupResultItem::Breadcrumb NativeType; + struct SerialType + { + NativeType::Kind kind; + NativeType::ThisParameterMode thisParameterMode; + ASTSerialTypeInfo<DeclRef<Decl>>::SerialType declRef; + ASTSerialTypeInfo<RefPtr<NativeType>> next; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + + dst.kind = src.kind; + dst.thisParameterMode = src.thisParameterMode; + _toSerialValue(writer, src.declRef, dst.declRef); + _toSerialValue(writer, src.next, dst.next); + } + + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& dst = *(NativeType*)native; + auto& src = *(const SerialType*)serial; + + dst.kind = src.kind; + dst.thisParameterMode = src.thisParameterMode; + _toNativeValue(reader, src.declRef, dst.declRef); + _toNativeValue(reader, src.next, dst.next); + } +}; + +// LookupResultItem +template <> +struct ASTSerialTypeInfo<LookupResultItem> +{ + typedef LookupResultItem NativeType; + struct SerialType + { + ASTSerialTypeInfo<DeclRef<Decl>>::SerialType declRef; + ASTSerialTypeInfo<RefPtr<NativeType::Breadcrumb>> breadcrumbs; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + + _toSerialValue(writer, src.declRef, dst.declRef); + _toSerialValue(writer, src.breadcrumbs, dst.breadcrumbs); + } + + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& dst = *(NativeType*)native; + auto& src = *(const SerialType*)serial; + + _toNativeValue(reader, src.declRef, dst.declRef); + _toNativeValue(reader, src.breadcrumbs, dst.breadcrumbs); + } +}; + +// LookupResult +template <> +struct ASTSerialTypeInfo<LookupResult> +{ + typedef LookupResult NativeType; + typedef ASTSerialIndex SerialType; + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + + if (src.isOverloaded()) + { + // Save off as an array + dst = writer->addArray(src.items.getBuffer(), src.items.getCount()); + } + else if (src.item.declRef.getDecl()) + { + dst = writer->addArray(&src.item, 1); + } + else + { + dst = ASTSerialIndex(0); + } + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& dst = *(NativeType*)native; + auto& src = *(const SerialType*)serial; + + // Initialize + dst = NativeType(); + + List<LookupResultItem> items; + reader->getArray(src, items); + + if (items.getCount() == 1) + { + dst.item = items[0]; + } + else + { + dst.items.swapWith(items); + // We have to set item such that it is valid/member of items, if items is non empty + dst.item = dst.items[0]; + } + } +}; + + +// GlobalGenericParamSubstitution::ConstraintArg +template <> +struct ASTSerialTypeInfo<GlobalGenericParamSubstitution::ConstraintArg> +{ + typedef GlobalGenericParamSubstitution::ConstraintArg NativeType; + struct SerialType + { + ASTSerialIndex decl; + ASTSerialIndex val; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& dst = *(SerialType*)serial; + auto& src = *(const NativeType*)native; + + dst.decl = writer->addPointer(src.decl); + dst.val = writer->addPointer(src.val); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; + + dst.decl = reader->getPointer(src.decl).dynamicCast<Decl>(); + dst.val = reader->getPointer(src.val).dynamicCast<Val>(); + } +}; + +// ExpandedSpecializationArg +template <> +struct ASTSerialTypeInfo<ExpandedSpecializationArg> +{ + typedef ExpandedSpecializationArg NativeType; + struct SerialType + { + ASTSerialIndex val; + ASTSerialIndex witness; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& dst = *(SerialType*)serial; + auto& src = *(const NativeType*)native; + + dst.witness = writer->addPointer(src.witness); + dst.val = writer->addPointer(src.val); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; + + dst.witness = reader->getPointer(src.witness).dynamicCast<Val>(); + dst.val = reader->getPointer(src.val).dynamicCast<Val>(); + } +}; + +// TypeExp +template <> +struct ASTSerialTypeInfo<TypeExp> +{ + typedef TypeExp NativeType; + struct SerialType + { + ASTSerialIndex type; + ASTSerialIndex expr; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& dst = *(SerialType*)serial; + auto& src = *(const NativeType*)native; + + dst.type = writer->addPointer(src.type); + dst.expr = writer->addPointer(src.exp); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; + + dst.type = reader->getPointer(src.type).dynamicCast<Type>(); + dst.exp = reader->getPointer(src.type).dynamicCast<Expr>(); + } +}; + +// DeclCheckStateExt +template <> +struct ASTSerialTypeInfo<DeclCheckStateExt> +{ + typedef DeclCheckStateExt NativeType; + typedef DeclCheckStateExt::RawType SerialType; + + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + SLANG_UNUSED(writer); + *(SerialType*)serial = (*(const NativeType*)native).getRaw(); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + SLANG_UNUSED(reader); + (*(NativeType*)serial).setRaw(*(const SerialType*)native); + } +}; + +// Modifiers +template <> +struct ASTSerialTypeInfo<Modifiers> +{ + typedef Modifiers NativeType; + typedef ASTSerialIndex SerialType; + + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + // We need to make into an array + List<ASTSerialIndex> modifierIndices; + for (Modifier* modifier : *(NativeType*)native) + { + modifierIndices.add(writer->addPointer(modifier)); + } + *(SerialType*)serial = writer->addArray(modifierIndices.getBuffer(), modifierIndices.getCount()); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + List<Modifier*> modifiers; + reader->getArray(*(const SerialType*)serial, modifiers); + + Modifier* prev = nullptr; + for (Modifier* modifier : modifiers) + { + if (prev) + { + prev->next = modifier; + } + } + + NativeType& dst = *(NativeType*)native; + dst.first = modifiers.getCount() > 0 ? modifiers[0] : nullptr; + } +}; + +// ImageFormat +template <> +struct ASTSerialTypeInfo<ImageFormat> : public ASTSerialConvertTypeInfo<ImageFormat, uint8_t> {}; + +// Stage +template <> +struct ASTSerialTypeInfo<Stage> : public ASTSerialConvertTypeInfo<Stage, uint8_t> {}; + +// TokenType +template <> +struct ASTSerialTypeInfo<TokenType> : public ASTSerialConvertTypeInfo<TokenType, uint8_t> {}; + +// BaseType +template <> +struct ASTSerialTypeInfo<BaseType> : public ASTSerialConvertTypeInfo<BaseType, uint8_t> {}; + +// SemanticVersion +template <> +struct ASTSerialTypeInfo<SemanticVersion> : public ASTSerialIdentityTypeInfo<SemanticVersion> {}; + +// ASTNodeType +template <> +struct ASTSerialTypeInfo<ASTNodeType> : public ASTSerialConvertTypeInfo<ASTNodeType, uint16_t> {}; + +// String +template <> +struct ASTSerialTypeInfo<String> +{ + typedef String NativeType; + typedef ASTSerialIndex SerialType; + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + *(SerialType*)serial = writer->addString(src); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; + dst = reader->getString(src); + } +}; + +// Token +template <> +struct ASTSerialTypeInfo<Token> +{ + typedef Token NativeType; + struct SerialType + { + ASTSerialTypeInfo<BaseType>::SerialType type; + ASTSerialTypeInfo<SourceLoc>::SerialType loc; + ASTSerialIndex name; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + + ASTSerialTypeInfo<TokenType>::toSerial(writer, &src.type, &dst.type); + ASTSerialTypeInfo<SourceLoc>::toSerial(writer, &src.loc, &dst.loc); + dst.name = writer->addName(src.getName()); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; + + dst.flags = 0; + dst.charsNameUnion.chars = nullptr; + + ASTSerialTypeInfo<TokenType>::toNative(reader, &src.type, &dst.type); + ASTSerialTypeInfo<SourceLoc>::toNative(reader, &src.loc, &dst.loc); + + if (src.name != ASTSerialIndex(0)) + { + dst.charsNameUnion.name = reader->getName(src.name); + dst.flags |= TokenFlag::Name; + } + } +}; + +// NameLoc +template <> +struct ASTSerialTypeInfo<NameLoc> +{ + typedef NameLoc NativeType; + struct SerialType + { + ASTSerialTypeInfo<SourceLoc>::SerialType loc; + ASTSerialIndex name; + }; + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + + dst.name = writer->addName(src.name); + ASTSerialTypeInfo<SourceLoc>::toSerial(writer, &src.loc, &dst.loc); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; + + dst.name = reader->getName(src.name); + ASTSerialTypeInfo<SourceLoc>::toNative(reader, &src.loc, &dst.loc); + } +}; + +// DiagnosticInfo +template <> +struct ASTSerialTypeInfo<const DiagnosticInfo*> +{ + typedef const DiagnosticInfo* NativeType; + typedef ASTSerialIndex SerialType; + + enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) }; + + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) + { + auto& src = *(const NativeType*)native; + auto& dst = *(SerialType*)serial; + dst = src ? writer->addString(UnownedStringSlice(src->name)) : ASTSerialIndex(0); + } + static void toNative(ASTSerialReader* reader, const void* serial, void* native) + { + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; + + if (src == ASTSerialIndex(0)) + { + dst = nullptr; + } + else + { + dst = findDiagnosticByName(reader->getStringSlice(src)); + } + } +}; + +// !!!!!!!!!!!!!!!!!!!!! ASTSerialGetType<T> !!!!!!!!!!!!!!!!!!!!!!!!!!! +// Getting the type info, let's use a static variable to hold the state to keep simple + +template <typename T> +struct ASTSerialGetType +{ + static const ASTSerialType* getType() + { + typedef ASTSerialTypeInfo<T> Info; + static const ASTSerialType type = { sizeof(typename Info::SerialType), uint8_t(Info::SerialAlignment), &Info::toSerial, &Info::toNative }; + return &type; + } +}; + +// Special case DeclRef, because it always uses the same type +template <typename T> +struct ASTSerialGetType<DeclRef<T>> +{ + static const ASTSerialType* getType() { return ASTSerialDeclRefBaseTypeInfo::getType(); } +}; + +// !!!!!!!!!!!!!!!!!!!!!! Generate fields for a type !!!!!!!!!!!!!!!!!!!!!!!!!!! + + +template <typename T> +ASTSerialField _calcField(const char* name, T& in) +{ + uint8_t* ptr = &reinterpret_cast<uint8_t&>(in); + + ASTSerialField field; + field.name = name; + field.type = ASTSerialGetType<T>::getType(); + // This only works because we in is an offset from 1 + field.nativeOffset = uint32_t(size_t(ptr) - 1); + field.serialOffset = 0; + return field; +} + +static ASTSerialClass _makeClass(MemoryArena* arena, ASTNodeType type, const List<ASTSerialField>& fields) +{ + ASTSerialClass cls = {}; + cls.type = type; + cls.fieldsCount = fields.getCount(); + cls.fields = arena->allocateAndCopyArray(fields.getBuffer(), fields.getCount()); + return cls; +} + +#define SLANG_AST_SERIAL_FIELD(FIELD_NAME, TYPE, param) fields.add(_calcField(#FIELD_NAME, obj->FIELD_NAME)); + +// Note that the obj point is not nullptr, because some compilers notice this is 'indexing from null' +// and warn/error. So we offset from 1. +#define SLANG_AST_SERIAL_MAKE_CLASS(NAME, SUPER, ORIGIN, LAST, MARKER, TYPE, param) \ +{ \ + NAME* obj = (NAME*)1; \ + SLANG_UNUSED(obj); \ + fields.clear(); \ + SLANG_FIELDS_ASTNode_##NAME(SLANG_AST_SERIAL_FIELD, param) \ + outClasses[Index(ASTNodeType::NAME)] = _makeClass(arena, ASTNodeType::NAME, fields); \ +} + +struct ASTFieldAccess +{ + static void calcClasses(MemoryArena* arena, ASTSerialClass outClasses[Index(ASTNodeType::CountOf)]) + { + List<ASTSerialField> fields; + SLANG_ALL_ASTNode_NodeBase(SLANG_AST_SERIAL_MAKE_CLASS, _) + } +}; + +ASTSerialClasses::ASTSerialClasses(): + m_arena(2048) +{ + memset(m_classes, 0, sizeof(m_classes)); + ASTFieldAccess::calcClasses(&m_arena, m_classes); + + // Now work out the layout + for (Index i = 0; i < SLANG_COUNT_OF(m_classes); ++i) + { + // Set up each class in order, from lowest to highest index + // Doing so means super class is always setup + ASTSerialClass& serialClass = m_classes[i]; + + const ReflectClassInfo* info = ReflectClassInfo::getInfo(serialClass.type); + + size_t maxAlignment = 1; + size_t offset = 0; + + const ReflectClassInfo* superInfo = info->m_superClass; + if (superInfo) + { + ASTSerialClass& superSerialInfo = m_classes[superInfo->m_classId]; + + // If it's been setup, then alignment must be non zero. + // The ordering of ASTNodeType, should mean type have larger ASTNodeType greater than supers ASTNodeType. + SLANG_ASSERT(superSerialInfo.alignment != 0); + + // Must be a power of 2 + SLANG_ASSERT((superSerialInfo.alignment & (superSerialInfo.alignment - 1)) == 0); + + maxAlignment = superSerialInfo.alignment; + offset = superSerialInfo.size; + + // Check it is correctly aligned + SLANG_ASSERT((offset & (maxAlignment - 1)) == 0); + } + + // Okay, go through fields setting their offset + ASTSerialField* field = serialClass.fields; + for (Index j = 0; j < serialClass.fieldsCount; j++) + { + size_t alignment = field->type->serialAlignment; + // Make sure the offset is aligned for the field requirement + offset = (offset + alignment - 1) & ~(alignment - 1); + + // Save the field offset + field->serialOffset = uint32_t(offset); + + // Move past the field + offset += field->type->serialSizeInBytes; + + // Calc the maximum alignment + maxAlignment = (alignment > maxAlignment) ? alignment : maxAlignment; + } + + // Align with maximum alignment + offset += (offset + maxAlignment - 1) & ~(maxAlignment - 1); + + serialClass.alignment = uint8_t(maxAlignment); + serialClass.size = uint32_t(offset); + } +} + + + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerialWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!! + +ASTSerialWriter::ASTSerialWriter(ASTSerialClasses* classes) : + m_arena(2048), + m_classes(classes) +{ + // 0 is always the null pointer + m_entries.add(nullptr); + m_ptrMap.Add(nullptr, 0); +} + +ASTSerialIndex ASTSerialWriter::addPointer(const NodeBase* node) +{ + // Null is always 0 + if (node == nullptr) + { + return ASTSerialIndex(0); + } + // Look up in the map + Index* indexPtr = m_ptrMap.TryGetValue(node); + if (indexPtr) + { + return ASTSerialIndex(*indexPtr); + } + + const ASTSerialClass* serialClass = m_classes->getSerialClass(node->astNodeType); + + typedef ASTSerialInfo::NodeEntry NodeEntry; + + NodeEntry* nodeEntry = (NodeEntry*)m_arena.allocateAligned(sizeof(NodeEntry) + serialClass->size, 8); + + nodeEntry->type = ASTSerialInfo::Type::Node; + nodeEntry->astNodeType = uint16_t(node->astNodeType); + nodeEntry->nextAlignment = 0; + + auto index = _add(node, nodeEntry); + + uint8_t* serialPayload = (uint8_t*)(nodeEntry + 1); + + while (serialClass) + { + for (Index i = 0; i < serialClass->fieldsCount; ++i) + { + auto field = serialClass->fields[i]; + // Work out the offsets + auto srcField = ((const uint8_t*)node) + field.nativeOffset; + auto dstField = serialPayload + field.serialOffset; + + field.type->toSerialFunc(this, srcField, dstField); + } + // Get the super class + const ReflectClassInfo* reflectInfo = ReflectClassInfo::getInfo(serialClass->type); + const ReflectClassInfo* superReflectInfo = reflectInfo->m_superClass; + + serialClass = superReflectInfo ? m_classes->getSerialClass(ASTNodeType(superReflectInfo->m_classId)) : nullptr; + } + + return index; +} + +ASTSerialIndex ASTSerialWriter::addPointer(const RefObject* obj) +{ + // Null is always 0 + if (obj == nullptr) + { + return ASTSerialIndex(0); + } + // Look up in the map + Index* indexPtr = m_ptrMap.TryGetValue(obj); + if (indexPtr) + { + return ASTSerialIndex(*indexPtr); + } + + if (auto stringRep = dynamicCast<StringRepresentation>(obj)) + { + ASTSerialIndex index = addString(StringRepresentation::asSlice(stringRep)); + m_ptrMap.Add(obj, Index(index)); + return index; + } + else if (auto breadcrumb = dynamicCast<LookupResultItem::Breadcrumb>(obj)) + { + typedef ASTSerialTypeInfo<LookupResultItem::Breadcrumb> TypeInfo; + typedef ASTSerialInfo::RefObjectEntry RefObjectEntry; + + RefObjectEntry* refEntry = (RefObjectEntry*)m_arena.allocateAligned(sizeof(RefObjectEntry) + sizeof(TypeInfo::SerialType), 8); + + refEntry->type = ASTSerialInfo::Type::RefObject; + refEntry->subType = RefObjectEntry::SubType::Breadcrumb; + + auto index = _add(breadcrumb, refEntry); + + // Do any conversion + TypeInfo::toSerial(this, breadcrumb, refEntry + 1); + return index; + } + else if (auto name = dynamicCast<const Name>(obj)) + { + return addName(name); + } + else if (auto scope = dynamicCast<Scope>(obj)) + { + // We don't serialize scope + return ASTSerialIndex(0); + } + else if (auto module = dynamicCast<Module>(obj)) + { + // We don't serialize Module + return ASTSerialIndex(0); + } + + SLANG_ASSERT(!"Unhandled type"); + return ASTSerialIndex(0); +} + +ASTSerialIndex ASTSerialWriter::addString(const UnownedStringSlice& slice) +{ + typedef ByteEncodeUtil Util; + typedef ASTSerialInfo::StringEntry StringEntry; + + if (slice.getLength() == 0) + { + return ASTSerialIndex(0); + } + + Index newIndex = m_entries.getCount(); + + Index* indexPtr = m_sliceMap.TryGetValueOrAdd(slice, newIndex); + if (indexPtr) + { + return ASTSerialIndex(*indexPtr); + } + + // Okay we need to add the string + + uint8_t encodeBuf[Util::kMaxLiteEncodeUInt32]; + const int encodeCount = Util::encodeLiteUInt32(uint32_t(slice.getLength()), encodeBuf); + + StringEntry* entry = (StringEntry*)m_arena.allocateUnaligned(sizeof(StringEntry) + encodeCount + slice.getLength()); + entry->nextAlignment = 0; + entry->type = ASTSerialInfo::Type::String; + + uint8_t* dst = (uint8_t*)(entry + 1); + for (int i = 0; i < encodeCount; ++i) + { + dst[i] = encodeBuf[i]; + } + + memcpy(dst + encodeCount, slice.begin(), slice.getLength()); + + m_entries.add(entry); + return ASTSerialIndex(newIndex); +} + + +ASTSerialIndex ASTSerialWriter::addString(const String& in) +{ + return addPointer(in.getStringRepresentation()); +} + +ASTSerialIndex ASTSerialWriter::addName(const Name* name) +{ + if (name == nullptr) + { + return ASTSerialIndex(0); + } + + // Look it up + Index* indexPtr = m_ptrMap.TryGetValue(name); + if (indexPtr) + { + return ASTSerialIndex(*indexPtr); + } + + ASTSerialIndex index = addString(name->text); + m_ptrMap.Add(name, Index(index)); + return index; +} + +ASTSerialSourceLoc ASTSerialWriter::addSourceLoc(SourceLoc sourceLoc) +{ + SLANG_UNUSED(sourceLoc); + return 0; +} + +ASTSerialIndex ASTSerialWriter::_addArray(size_t elementSize, const void* elements, Index elementCount) +{ + typedef ASTSerialInfo::ArrayEntry Entry; + + if (elementCount == 0) + { + return ASTSerialIndex(0); + } + + size_t payloadSize = elementCount * elementSize; + + Entry* entry = (Entry*)m_arena.allocateAligned(sizeof(Entry) + payloadSize, 8); + + entry->type = ASTSerialInfo::Type::Array; + entry->nextAlignment = 0; + entry->elementSize = uint16_t(elementSize); + entry->elementCount = uint32_t(elementCount); + + memcpy(entry + 1, elements, payloadSize); + + m_entries.add(entry); + ASTSerialIndex index = ASTSerialIndex(m_entries.getCount() - 1); + + // We don't add to a pointer map, because arrays are not shared + + // Do the conversion + + return index; +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerialReader !!!!!!!!!!!!!!!!!!!!!!!!!!!! + +ASTSerialPointer ASTSerialReader::getPointer(ASTSerialIndex index) +{ + SLANG_UNUSED(index); + return ASTSerialPointer(); +} + +String ASTSerialReader::getString(ASTSerialIndex index) +{ + SLANG_UNUSED(index); + return String(); +} + +Name* ASTSerialReader::getName(ASTSerialIndex index) +{ + if (index == ASTSerialIndex(0)) + { + return nullptr; + } + return nullptr; +} + +UnownedStringSlice ASTSerialReader::getStringSlice(ASTSerialIndex index) +{ + SLANG_UNUSED(index); + return UnownedStringSlice(); +} + +SourceLoc ASTSerialReader::getSourceLoc(ASTSerialSourceLoc loc) +{ + SLANG_UNUSED(loc); + return SourceLoc(); +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerializeUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!! + +/* static */SlangResult ASTSerializeUtil::selfTest() +{ + RefPtr<ASTSerialClasses> classes = new ASTSerialClasses; + + { + struct Thing + { + Module* node; + }; + Thing thing; + + //Pointer pointer(thing.node); + + auto field = _calcField("node", thing.node); + + + const ASTSerialType* type = ASTSerialGetType<Type*>::getType(); + SLANG_UNUSED(type); + } + + { + const ASTSerialType* type = ASTSerialGetType<int[10]>::getType(); + SLANG_UNUSED(type); + } + + { + const ASTSerialType* type = ASTSerialGetType<bool[3]>::getType(); + SLANG_UNUSED(type); + } + + { + const ASTSerialType* type = ASTSerialGetType<Type*[3]>::getType(); + SLANG_UNUSED(type); + } + + return SLANG_OK; +} + + +} // namespace Slang diff --git a/source/slang/slang-ast-serialize.h b/source/slang/slang-ast-serialize.h new file mode 100644 index 000000000..a20da2e37 --- /dev/null +++ b/source/slang/slang-ast-serialize.h @@ -0,0 +1,254 @@ +// slang-ast-serialize.h +#ifndef SLANG_AST_SERIALIZE_H +#define SLANG_AST_SERIALIZE_H + +#include <type_traits> + +#include "slang-ast-support-types.h" +#include "slang-ast-all.h" + +namespace Slang +{ + +// Type used to implement mechanisms to convert to and from serial types. +template <typename T> +struct ASTSerialTypeInfo; + +struct ASTSerialInfo +{ + enum class Type : uint8_t + { + String, ///< String + Node, ///< NodeBase derived + RefObject, ///< RefObject derived types + Array, ///< Array + }; + + struct Entry + { + Type type; + uint8_t nextAlignment; ///< Alignment of next entry + }; + + struct StringEntry : Entry + { + }; + + struct NodeEntry : Entry + { + uint16_t astNodeType; + uint32_t _pad0; + }; + + struct RefObjectEntry : Entry + { + enum class SubType : uint8_t + { + Breadcrumb, + }; + SubType subType; + uint32_t _pad0; + }; + + struct ArrayEntry : Entry + { + uint16_t elementSize; + uint32_t elementCount; + }; +}; + +enum class ASTSerialIndex : uint32_t; +typedef uint32_t ASTSerialSourceLoc; + +/* A type to convert pointers into types such that they can be passed around to readers/writers without +having to know the specific type. If there was a base class that all the serialized types derived from, +that was dynamically castable this would not be necessary */ +struct ASTSerialPointer +{ + enum class Kind + { + Unknown, + RefObject, + NodeBase + }; + + // Helpers so we can choose what kind of pointer we have based on the (unused) type of the pointer passed in + SLANG_FORCE_INLINE RefObject* _get(const RefObject*) { return m_kind == Kind::RefObject ? reinterpret_cast<RefObject*>(m_ptr) : nullptr; } + SLANG_FORCE_INLINE NodeBase* _get(const NodeBase*) { return m_kind == Kind::NodeBase ? reinterpret_cast<NodeBase*>(m_ptr) : nullptr; } + + template <typename T> + T* dynamicCast() + { + return Slang::dynamicCast<T>(_get((T*)nullptr)); + } + + ASTSerialPointer() : + m_kind(Kind::Unknown), + m_ptr(nullptr) + { + } + + ASTSerialPointer(RefObject* in) : + m_kind(Kind::RefObject), + m_ptr((void*)in) + { + } + ASTSerialPointer(NodeBase* in) : + m_kind(Kind::NodeBase), + m_ptr((void*)in) + { + } + + static Kind getKind(const RefObject*) { return Kind::RefObject; } + static Kind getKind(const NodeBase*) { return Kind::NodeBase; } + + Kind m_kind; + void* m_ptr; +}; + + +/* This class is the interface used by toNative implementations to recreate a type */ +class ASTSerialReader : public RefObject +{ +public: + + ASTSerialPointer getPointer(ASTSerialIndex index); + template <typename T> + void getArray(ASTSerialIndex index, List<T>& outArray); + String getString(ASTSerialIndex index); + Name* getName(ASTSerialIndex index); + UnownedStringSlice getStringSlice(ASTSerialIndex index); + SourceLoc getSourceLoc(ASTSerialSourceLoc loc); +}; + +// --------------------------------------------------------------------------- +template <typename T> +void ASTSerialReader::getArray(ASTSerialIndex index, List<T>& outArray) +{ + SLANG_UNUSED(index); + outArray.clear(); +} + +class ASTSerialClasses; + +/* This is a class used tby toSerial implementations to turn native type into the serial type */ +class ASTSerialWriter : public RefObject +{ +public: + ASTSerialIndex addPointer(const NodeBase* ptr); + ASTSerialIndex addPointer(const RefObject* ptr); + + template <typename T> + ASTSerialIndex addArray(const T* in, Index count); + + ASTSerialIndex addString(const UnownedStringSlice& slice); + ASTSerialIndex addString(const String& in); + ASTSerialIndex addName(const Name* name); + ASTSerialSourceLoc addSourceLoc(SourceLoc sourceLoc); + + ASTSerialWriter(ASTSerialClasses* classes); + +protected: + + ASTSerialIndex _addArray(size_t elementSize, const void* elements, Index elementCount); + + ASTSerialIndex _add(const void* nativePtr, ASTSerialInfo::Entry* entry) + { + m_entries.add(entry); + // Okay I need to allocate space for this + ASTSerialIndex index = ASTSerialIndex(m_entries.getCount() - 1); + // Add to the map + m_ptrMap.Add(nativePtr, Index(index)); + return index; + } + + Dictionary<const void*, Index> m_ptrMap; // Maps a pointer to an entry index + + // NOTE! Assumes the content stays in scope! + Dictionary<UnownedStringSlice, Index> m_sliceMap; + + List<ASTSerialInfo::Entry*> m_entries; ///< The entries + MemoryArena m_arena; ///< Holds the payloads + ASTSerialClasses* m_classes; +}; + +// --------------------------------------------------------------------------- +template <typename T> +ASTSerialIndex ASTSerialWriter::addArray(const T* in, Index count) +{ + typedef ASTSerialTypeInfo<T> ElementTypeInfo; + typedef typename ElementTypeInfo::SerialType ElementSerialType; + + if (std::is_same<T, ElementSerialType>::value) + { + // If they are the same we can just write out + return _addArray(sizeof(T), in, count); + } + else + { + // Else we need to convert + List<ElementSerialType> work; + work.setCount(count); + + for (Index i = 0; i < count; ++i) + { + ElementTypeInfo::toSerial(this, &in[i], &work[i]); + } + return _addArray(sizeof(ElementSerialType), in, count); + } +} + +struct ASTSerialType +{ + typedef void(*ToSerialFunc)(ASTSerialWriter* writer, const void* src, void* dst); + typedef void(*ToNativeFunc)(ASTSerialReader* reader, const void* src, void* dst); + + size_t serialSizeInBytes; + uint8_t serialAlignment; + ToSerialFunc toSerialFunc; + ToNativeFunc toNativeFunc; +}; + +struct ASTSerialField +{ + const char* name; ///< The name of the field + const ASTSerialType* type; ///< The type of the field + uint32_t nativeOffset; ///< Offset to field from base of type + uint32_t serialOffset; ///< Offset in serial type +}; + + +struct ASTSerialClass +{ + ASTNodeType type; + uint8_t alignment; + ASTSerialField* fields; + Index fieldsCount; + uint32_t size; +}; + +// An instance could be shared across Sessions, but for simplicity of life time +// here we don't deal with that +class ASTSerialClasses : public RefObject +{ +public: + + const ASTSerialClass* getSerialClass(ASTNodeType type) const { return &m_classes[Index(type)]; } + + /// Ctor + ASTSerialClasses(); + +protected: + MemoryArena m_arena; + + ASTSerialClass m_classes[Index(ASTNodeType::CountOf)]; +}; + +struct ASTSerializeUtil +{ + static SlangResult selfTest(); +}; + +} // namespace Slang + +#endif diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 66144eaed..6fe8781c5 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -393,12 +393,13 @@ namespace Slang struct DeclCheckStateExt { public: + typedef uint8_t RawType; DeclCheckStateExt() {} DeclCheckStateExt(DeclCheckState state) : m_raw(uint8_t(state)) {} - enum : uint8_t + enum : RawType { /// A flag to indicate that a declaration is being checked. /// @@ -412,7 +413,7 @@ namespace Slang DeclCheckState getState() const { return DeclCheckState(m_raw & ~kBeingCheckedBit); } void setState(DeclCheckState state) { - m_raw = (m_raw & kBeingCheckedBit) | uint8_t(state); + m_raw = (m_raw & kBeingCheckedBit) | RawType(state); } bool isBeingChecked() const { return (m_raw & kBeingCheckedBit) != 0; } @@ -428,8 +429,11 @@ namespace Slang return getState() >= state; } + RawType getRaw() const { return m_raw; } + void setRaw(RawType raw) { m_raw = raw; } + private: - uint8_t m_raw = 0; + RawType m_raw = 0; }; void addModifier( @@ -1179,12 +1183,16 @@ namespace Slang // in the case where the result is overloaded. struct LookupResult { - // The one item that was found, in the smple case + // The one item that was found, in the simple case LookupResultItem item; // All of the items that were found, in the complex case. // Note: if there was no overloading, then this list isn't // used at all, to avoid allocation. + // + // Additionally, if `items` is used, then `item` *must* hold an item that + // is also in the items list (typically the first entry), as an invariant. + // Otherwise isValid/begin will not function correctly. List<LookupResultItem> items; // Was at least one result found? diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index f6d38889b..5349f0e30 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -22,7 +22,7 @@ // -1 - Notes that decorate another diagnostic. // -DIAGNOSTIC(-1, Note, alsoSeePipelineDefinition, "also see pipeline definition"); +DIAGNOSTIC(-1, Note, alsoSeePipelineDefinition, "also see pipeline definition") DIAGNOSTIC(-1, Note, implicitParameterMatchingFailedBecauseNameNotAccessible, "implicit parameter matching failed because the component of the same name is not accessible from '$0'.\ncheck if you have declared necessary requirements and properly used the 'public' qualifier.") DIAGNOSTIC(-1, Note, implicitParameterMatchingFailedBecauseShaderDoesNotDefineComponent, "implicit parameter matching failed because shader '$0' does not define component '$1'.") DIAGNOSTIC(-1, Note, implicitParameterMatchingFailedBecauseTypeMismatch, "implicit parameter matching failed because the component of the same name does not match parameter type '$0'.") @@ -62,25 +62,25 @@ DIAGNOSTIC( 8, Error, outputPathsImplyDifferentFormats, "the output paths '$0' and '$1' require different code-generation targets") DIAGNOSTIC( 10, Error, explicitOutputPathsAndMultipleTargets, "canot use both explicit output paths ('-o') and multiple targets ('-target')") -DIAGNOSTIC( 11, Error, glslIsNotSupported, "the Slang compiler does not support GLSL as a source language"); -DIAGNOSTIC( 12, Error, cannotDeduceSourceLanguage, "can't deduce language for input file '$0'"); -DIAGNOSTIC( 13, Error, unknownCodeGenerationTarget, "unknown code generation target '$0'"); -DIAGNOSTIC( 14, Error, unknownProfile, "unknown profile '$0'"); -DIAGNOSTIC( 15, Error, unknownStage, "unknown stage '$0'"); -DIAGNOSTIC( 16, Error, unknownPassThroughTarget, "unknown pass-through target '$0'"); -DIAGNOSTIC( 17, Error, unknownCommandLineOption, "unknown command-line option '$0'"); -DIAGNOSTIC( 18, Error, unknownFileSystemOption, "unknown file-system option '$0'"); -DIAGNOSTIC( 19, Error, unknownSourceLanguage, "unknown source language '$0'"); - -DIAGNOSTIC( 20, Error, entryPointsNeedToBeAssociatedWithTranslationUnits, "when using multiple source files, entry points must be specified after their corresponding source file(s)"); -DIAGNOSTIC( 21, Error, expectedArgumentForOption, "expected an argument for command-line option '$0'"); - -DIAGNOSTIC( 24, Error, unknownLineDirectiveMode, "unknown '#line' directive mode '$0'"); -DIAGNOSTIC( 25, Error, unknownFloatingPointMode, "unknown floating-point mode '$0'"); -DIAGNOSTIC( 26, Error, unknownOptimiziationLevel, "unknown optimization level '$0'"); -DIAGNOSTIC( 27, Error, unknownDebugInfoLevel, "unknown debug info level '$0'"); - -DIAGNOSTIC( 28, Error, unableToGenerateCodeForTarget, "unable to generate code for target '$0'"); +DIAGNOSTIC( 11, Error, glslIsNotSupported, "the Slang compiler does not support GLSL as a source language") +DIAGNOSTIC( 12, Error, cannotDeduceSourceLanguage, "can't deduce language for input file '$0'") +DIAGNOSTIC( 13, Error, unknownCodeGenerationTarget, "unknown code generation target '$0'") +DIAGNOSTIC( 14, Error, unknownProfile, "unknown profile '$0'") +DIAGNOSTIC( 15, Error, unknownStage, "unknown stage '$0'") +DIAGNOSTIC( 16, Error, unknownPassThroughTarget, "unknown pass-through target '$0'") +DIAGNOSTIC( 17, Error, unknownCommandLineOption, "unknown command-line option '$0'") +DIAGNOSTIC( 18, Error, unknownFileSystemOption, "unknown file-system option '$0'") +DIAGNOSTIC( 19, Error, unknownSourceLanguage, "unknown source language '$0'") + +DIAGNOSTIC( 20, Error, entryPointsNeedToBeAssociatedWithTranslationUnits, "when using multiple source files, entry points must be specified after their corresponding source file(s)") +DIAGNOSTIC( 21, Error, expectedArgumentForOption, "expected an argument for command-line option '$0'") + +DIAGNOSTIC( 24, Error, unknownLineDirectiveMode, "unknown '#line' directive mode '$0'") +DIAGNOSTIC( 25, Error, unknownFloatingPointMode, "unknown floating-point mode '$0'") +DIAGNOSTIC( 26, Error, unknownOptimiziationLevel, "unknown optimization level '$0'") +DIAGNOSTIC( 27, Error, unknownDebugInfoLevel, "unknown debug info level '$0'") + +DIAGNOSTIC( 28, Error, unableToGenerateCodeForTarget, "unable to generate code for target '$0'") DIAGNOSTIC( 30, Warning, sameStageSpecifiedMoreThanOnce, "the stage '$0' was specified more than once for entry point '$1'") DIAGNOSTIC( 31, Error, conflictingStagesForEntryPoint, "conflicting stages have been specified for entry point '$0'") @@ -109,27 +109,27 @@ DIAGNOSTIC( 70, Error, cannotMatchOutputFileToEntryPoint, "the output path '$ DIAGNOSTIC( 80, Error, duplicateOutputPathsForEntryPointAndTarget, "multiple output paths have been specified entry point '$0' on target '$1'") -DIAGNOSTIC( 82, Error, unableToWriteReproFile, "unable to write repro file '%0'"); -DIAGNOSTIC( 83, Error, unableToWriteModuleContainer, "unable to write module container '%0'"); -DIAGNOSTIC( 84, Error, unableToReadModuleContainer, "unable to read module container '%0'"); -DIAGNOSTIC( 85, Error, unableToAddReferenceToModuleContainer, "unable to add a reference to a module container"); -DIAGNOSTIC( 86, Error, unableToCreateModuleContainer, "unable to create module container"); +DIAGNOSTIC( 82, Error, unableToWriteReproFile, "unable to write repro file '%0'") +DIAGNOSTIC( 83, Error, unableToWriteModuleContainer, "unable to write module container '%0'") +DIAGNOSTIC( 84, Error, unableToReadModuleContainer, "unable to read module container '%0'") +DIAGNOSTIC( 85, Error, unableToAddReferenceToModuleContainer, "unable to add a reference to a module container") +DIAGNOSTIC( 86, Error, unableToCreateModuleContainer, "unable to create module container") -DIAGNOSTIC( 87, Error, unableToSetDefaultDownstreamCompiler, "unable to set default downstream compiler for source language '%0' to '%1'"); +DIAGNOSTIC( 87, Error, unableToSetDefaultDownstreamCompiler, "unable to set default downstream compiler for source language '%0' to '%1'") // // 1xxxx - Lexical analysis // -DIAGNOSTIC(10000, Error, illegalCharacterPrint, "illegal character '$0'"); -DIAGNOSTIC(10000, Error, illegalCharacterHex, "illegal character (0x$0)"); -DIAGNOSTIC(10001, Error, illegalCharacterLiteral, "illegal character literal"); +DIAGNOSTIC(10000, Error, illegalCharacterPrint, "illegal character '$0'") +DIAGNOSTIC(10000, Error, illegalCharacterHex, "illegal character (0x$0)") +DIAGNOSTIC(10001, Error, illegalCharacterLiteral, "illegal character literal") DIAGNOSTIC(10002, Warning, octalLiteral, "'0' prefix indicates octal literal") DIAGNOSTIC(10003, Error, invalidDigitForBase, "invalid digit for base-$1 literal: '$0'") -DIAGNOSTIC(10004, Error, endOfFileInLiteral, "end of file in literal"); -DIAGNOSTIC(10005, Error, newlineInLiteral, "newline in literal"); +DIAGNOSTIC(10004, Error, endOfFileInLiteral, "end of file in literal") +DIAGNOSTIC(10005, Error, newlineInLiteral, "newline in literal") // // 15xxx - Preprocessing @@ -151,11 +151,11 @@ DIAGNOSTIC(15103, Error, unexpectedTokensAfterDirective, "unexpected tokens foll // 152xx - preprocessor expressions -DIAGNOSTIC(15200, Error, expectedTokenInPreprocessorExpression, "expected '$0' in preprocessor expression"); -DIAGNOSTIC(15201, Error, syntaxErrorInPreprocessorExpression, "syntax error in preprocessor expression"); -DIAGNOSTIC(15202, Error, divideByZeroInPreprocessorExpression, "division by zero in preprocessor expression"); -DIAGNOSTIC(15203, Error, expectedTokenInDefinedExpression, "expected '$0' in 'defined' expression"); -DIAGNOSTIC(15204, Warning, directiveExpectsExpression, "'$0' directive requires an expression"); +DIAGNOSTIC(15200, Error, expectedTokenInPreprocessorExpression, "expected '$0' in preprocessor expression") +DIAGNOSTIC(15201, Error, syntaxErrorInPreprocessorExpression, "syntax error in preprocessor expression") +DIAGNOSTIC(15202, Error, divideByZeroInPreprocessorExpression, "division by zero in preprocessor expression") +DIAGNOSTIC(15203, Error, expectedTokenInDefinedExpression, "expected '$0' in 'defined' expression") +DIAGNOSTIC(15204, Warning, directiveExpectsExpression, "'$0' directive requires an expression") DIAGNOSTIC(15205, Warning, undefinedIdentifierInPreprocessorExpression, "undefined identifier '$0' in preprocessor expression will evaluate to zero") DIAGNOSTIC(-1, Note, seeOpeningToken, "see opening '$0'") @@ -190,22 +190,22 @@ DIAGNOSTIC(15901, Warning, userDefinedWarning, "#warning: $0") // 2xxxx - Parsing // -DIAGNOSTIC(20003, Error, unexpectedToken, "unexpected $0"); -DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenType, "unexpected $0, expected $1"); -DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenName, "unexpected $0, expected '$1'"); - -DIAGNOSTIC(0, Error, tokenNameExpectedButEOF, "\"$0\" expected but end of file encountered."); -DIAGNOSTIC(0, Error, tokenTypeExpectedButEOF, "$0 expected but end of file encountered."); -DIAGNOSTIC(20001, Error, tokenNameExpected, "\"$0\" expected"); -DIAGNOSTIC(20001, Error, tokenNameExpectedButEOF2, "\"$0\" expected but end of file encountered."); -DIAGNOSTIC(20001, Error, tokenTypeExpected, "$0 expected"); -DIAGNOSTIC(20001, Error, tokenTypeExpectedButEOF2, "$0 expected but end of file encountered."); -DIAGNOSTIC(20001, Error, typeNameExpectedBut, "unexpected $0, expected type name"); -DIAGNOSTIC(20001, Error, typeNameExpectedButEOF, "type name expected but end of file encountered."); -DIAGNOSTIC(20001, Error, unexpectedEOF, " Unexpected end of file."); -DIAGNOSTIC(20002, Error, syntaxError, "syntax error."); +DIAGNOSTIC(20003, Error, unexpectedToken, "unexpected $0") +DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenType, "unexpected $0, expected $1") +DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenName, "unexpected $0, expected '$1'") + +DIAGNOSTIC(0, Error, tokenNameExpectedButEOF, "\"$0\" expected but end of file encountered.") +DIAGNOSTIC(0, Error, tokenTypeExpectedButEOF, "$0 expected but end of file encountered.") +DIAGNOSTIC(20001, Error, tokenNameExpected, "\"$0\" expected") +DIAGNOSTIC(20001, Error, tokenNameExpectedButEOF2, "\"$0\" expected but end of file encountered.") +DIAGNOSTIC(20001, Error, tokenTypeExpected, "$0 expected") +DIAGNOSTIC(20001, Error, tokenTypeExpectedButEOF2, "$0 expected but end of file encountered.") +DIAGNOSTIC(20001, Error, typeNameExpectedBut, "unexpected $0, expected type name") +DIAGNOSTIC(20001, Error, typeNameExpectedButEOF, "type name expected but end of file encountered.") +DIAGNOSTIC(20001, Error, unexpectedEOF, " Unexpected end of file.") +DIAGNOSTIC(20002, Error, syntaxError, "syntax error.") DIAGNOSTIC(20004, Error, unexpectedTokenExpectedComponentDefinition, "unexpected token '$0', only component definitions are allowed in a shader scope.") -DIAGNOSTIC(20008, Error, invalidOperator, "invalid operator '$0'."); +DIAGNOSTIC(20008, Error, invalidOperator, "invalid operator '$0'.") DIAGNOSTIC(20011, Error, unexpectedColon, "unexpected ':'.") DIAGNOSTIC(20012, Error, invalidSPIRVVersion, "Expecting SPIR-V version as either 'major.minor', or quoted if has patch (eg for SPIR-V 1.2, '1.2' or \"1.2\"')") DIAGNOSTIC(20013, Error, invalidCUDASMVersion, "Expecting CUDA SM version as either 'major.minor', or quoted if has patch (eg for '7.0' or \"7.0\"')") @@ -234,10 +234,10 @@ DIAGNOSTIC(30019, Error, typeMismatch, "expected an expression of type '$0', got DIAGNOSTIC(30020, Error, importOperatorReturnTypeMismatch, "import operator should return '$1', but the expression has type '$0''. do you forget 'project'?") DIAGNOSTIC(30021, Error, noApplicationFunction, "$0: no overload takes arguments ($1)") DIAGNOSTIC(30022, Error, invalidTypeCast, "invalid type cast between \"$0\" and \"$1\".") -DIAGNOSTIC(30023, Error, typeHasNoPublicMemberOfName, "\"$0\" does not have public member \"$1\"."); +DIAGNOSTIC(30023, Error, typeHasNoPublicMemberOfName, "\"$0\" does not have public member \"$1\".") DIAGNOSTIC(30025, Error, invalidArraySize, "array size must be larger than zero.") DIAGNOSTIC(30026, Error, returnInComponentMustComeLast, "'return' can only appear as the last statement in component definition.") -DIAGNOSTIC(30027, Error, noMemberOfNameInType, "'$0' is not a member of '$1'."); +DIAGNOSTIC(30027, Error, noMemberOfNameInType, "'$0' is not a member of '$1'.") DIAGNOSTIC(30028, Error, forPredicateTypeError, "'for': predicate expression must evaluate to bool.") DIAGNOSTIC(30030, Error, projectionOutsideImportOperator, "'project': invalid use outside import operator.") DIAGNOSTIC(30031, Error, projectTypeMismatch, "'project': expression must evaluate to record type '$0'.") @@ -272,7 +272,7 @@ DIAGNOSTIC(31000, Error, unknownAttributeName, "unknown attribute '$0'") DIAGNOSTIC(31001, Error, attributeArgumentCountMismatch, "attribute '$0' expects $1 arguments ($2 provided)") DIAGNOSTIC(31002, Error, attributeNotApplicable, "attribute '$0' is not valid here") -DIAGNOSTIC(31003, Error, badlyDefinedPatchConstantFunc, "hull shader '$0' has has badly defined 'patchconstantfunc' attribute."); +DIAGNOSTIC(31003, Error, badlyDefinedPatchConstantFunc, "hull shader '$0' has has badly defined 'patchconstantfunc' attribute.") DIAGNOSTIC(31004, Error, expectedSingleIntArg, "attribute '$0' expects a single int argument") DIAGNOSTIC(31005, Error, expectedSingleStringArg, "attribute '$0' expects a single string argument") @@ -305,19 +305,19 @@ DIAGNOSTIC(39999, Error, variableUsedInItsOwnDefinition, "the initial-value expr DIAGNOSTIC(30400, Error, genericTypeNeedsArgs, "generic type '$0' used without argument") // 305xx: initializer lists -DIAGNOSTIC(30500, Error, tooManyInitializers, "too many initializers (expected $0, got $1)"); -DIAGNOSTIC(30501, Error, cannotUseInitializerListForArrayOfUnknownSize, "cannot use initializer list for array of statically unknown size '$0'"); -DIAGNOSTIC(30502, Error, cannotUseInitializerListForVectorOfUnknownSize, "cannot use initializer list for vector of statically unknown size '$0'"); -DIAGNOSTIC(30503, Error, cannotUseInitializerListForMatrixOfUnknownSize, "cannot use initializer list for matrix of statically unknown size '$0' rows"); +DIAGNOSTIC(30500, Error, tooManyInitializers, "too many initializers (expected $0, got $1)") +DIAGNOSTIC(30501, Error, cannotUseInitializerListForArrayOfUnknownSize, "cannot use initializer list for array of statically unknown size '$0'") +DIAGNOSTIC(30502, Error, cannotUseInitializerListForVectorOfUnknownSize, "cannot use initializer list for vector of statically unknown size '$0'") +DIAGNOSTIC(30503, Error, cannotUseInitializerListForMatrixOfUnknownSize, "cannot use initializer list for matrix of statically unknown size '$0' rows") DIAGNOSTIC(30504, Error, cannotUseInitializerListForType, "cannot use initializer list for type '$0'") // 306xx: variables -DIAGNOSTIC(30600, Error, varWithoutTypeMustHaveInitializer, "a variable declaration without an initial-value expression must be given an explicit type"); +DIAGNOSTIC(30600, Error, varWithoutTypeMustHaveInitializer, "a variable declaration without an initial-value expression must be given an explicit type") DIAGNOSTIC(30610, Error, ambiguousDefaultInitializerForType, "more than one default initializer was found for type '$0'") // 307xx: parameters -DIAGNOSTIC(30700, Error, outputParameterCannotHaveDefaultValue, "an 'out' or 'inout' parameter cannot have a default-value expression"); +DIAGNOSTIC(30700, Error, outputParameterCannotHaveDefaultValue, "an 'out' or 'inout' parameter cannot have a default-value expression") // 308xx: inheritance DIAGNOSTIC(30810, Error, baseOfInterfaceMustBeInterface, "interface '$0' cannot inherit from non-interface type '$1'") @@ -354,10 +354,10 @@ DIAGNOSTIC(39999, Error, expectedAGeneric, "expected a generic when using '<...> DIAGNOSTIC(39999, Error, genericArgumentInferenceFailed, "could not specialize generic for arguments of type $0") DIAGNOSTIC(39999, Note, genericSignatureTried, "see declaration of $0") -DIAGNOSTIC(39999, Error, ambiguousReference, "ambiguous reference to '$0'"); -DIAGNOSTIC(39999, Error, ambiguousExpression, "ambiguous reference"); +DIAGNOSTIC(39999, Error, ambiguousReference, "ambiguous reference to '$0'") +DIAGNOSTIC(39999, Error, ambiguousExpression, "ambiguous reference") -DIAGNOSTIC(39999, Error, declarationDidntDeclareAnything, "declaration does not declare anything"); +DIAGNOSTIC(39999, Error, declarationDidntDeclareAnything, "declaration does not declare anything") DIAGNOSTIC(39999, Error, expectedPrefixOperator, "function called as prefix operator was not declared `__prefix`") DIAGNOSTIC(39999, Error, expectedPostfixOperator, "function called as postfix operator was not declared `__postfix`") @@ -403,7 +403,7 @@ DIAGNOSTIC(38022, Error, cannotSpecializeGlobalGenericToItself, "the global type DIAGNOSTIC(38023, Error, cannotSpecializeGlobalGenericToAnotherGenericParam, "the global type parameter '$0' cannot be specialized using another global type parameter ('$1')") -DIAGNOSTIC(38024, Error, invalidDispatchThreadIDType, "parameter with SV_DispatchThreadID must be either scalar or vector (1 to 3) of uint/int but is $0"); +DIAGNOSTIC(38024, Error, invalidDispatchThreadIDType, "parameter with SV_DispatchThreadID must be either scalar or vector (1 to 3) of uint/int but is $0") DIAGNOSTIC(-1, Note, noteWhenCompilingEntryPoint, "when compiling entry point '$0'") @@ -446,10 +446,10 @@ DIAGNOSTIC(39016, Error, globalUniformsNotSupported, "'$0' is implicitly a globa DIAGNOSTIC(39017, Error, tooManyShaderRecordConstantBuffers, "can have at most one 'shader record' attributed constant buffer; found $0.") -DIAGNOSTIC(39018, Error, typeParametersNotAllowedOnEntryPointGlobal, "local-root-signature shader parameter '$0' at global scope must not include existential/interface types"); +DIAGNOSTIC(39018, Error, typeParametersNotAllowedOnEntryPointGlobal, "local-root-signature shader parameter '$0' at global scope must not include existential/interface types") DIAGNOSTIC(39019, Warning, vkIndexWithoutVkLocation, "ignoring '[[vk::index(...)]]` attribute without a corresponding '[[vk::location(...)]]' attribute") -DIAGNOSTIC(39020, Error, mixingImplicitAndExplicitBindingForVaryingParams, "mixing explicit and implicit bindings for varying parameters is not supported (see '$0' and '$1')"); +DIAGNOSTIC(39020, Error, mixingImplicitAndExplicitBindingForVaryingParams, "mixing explicit and implicit bindings for varying parameters is not supported (see '$0' and '$1')") // // 4xxxx - IL code generation. @@ -479,7 +479,7 @@ DIAGNOSTIC(41010, Warning, missingReturn, "control flow may reach end of non-'vo // 5xxxx - Target code generation. // -DIAGNOSTIC(50010, Internal, missingExistentialBindingsForParameter, "missing argument for existential parameter slot"); +DIAGNOSTIC(50010, Internal, missingExistentialBindingsForParameter, "missing argument for existential parameter slot") DIAGNOSTIC(50020, Error, invalidTessCoordType, "TessCoord must have vec2 or vec3 type.") DIAGNOSTIC(50020, Error, invalidFragCoordType, "FragCoord must be a vec4.") @@ -487,51 +487,51 @@ DIAGNOSTIC(50020, Error, invalidInvocationIdType, "InvocationId must have DIAGNOSTIC(50020, Error, invalidThreadIdType, "ThreadId must have int type.") DIAGNOSTIC(50020, Error, invalidPrimitiveIdType, "PrimitiveId must have int type.") DIAGNOSTIC(50020, Error, invalidPatchVertexCountType, "PatchVertexCount must have int type.") -DIAGNOSTIC(50022, Error, worldIsNotDefined, "world '$0' is not defined."); -DIAGNOSTIC(50023, Error, stageShouldProvideWorldAttribute, "'$0' should provide 'World' attribute."); +DIAGNOSTIC(50022, Error, worldIsNotDefined, "world '$0' is not defined.") +DIAGNOSTIC(50023, Error, stageShouldProvideWorldAttribute, "'$0' should provide 'World' attribute.") DIAGNOSTIC(50040, Error, componentHasInvalidTypeForPositionOutput, "'$0': component used as 'loc' output must be of vec4 type.") DIAGNOSTIC(50041, Error, componentNotDefined, "'$0': component not defined.") -DIAGNOSTIC(50052, Error, domainShaderRequiresControlPointCount, "'DomainShader' requires attribute 'ControlPointCount'."); +DIAGNOSTIC(50052, Error, domainShaderRequiresControlPointCount, "'DomainShader' requires attribute 'ControlPointCount'.") DIAGNOSTIC(50052, Error, hullShaderRequiresControlPointCount, "'HullShader' requires attribute 'ControlPointCount'.") -DIAGNOSTIC(50052, Error, hullShaderRequiresControlPointWorld, "'HullShader' requires attribute 'ControlPointWorld'."); -DIAGNOSTIC(50052, Error, hullShaderRequiresCornerPointWorld, "'HullShader' requires attribute 'CornerPointWorld'."); -DIAGNOSTIC(50052, Error, hullShaderRequiresDomain, "'HullShader' requires attribute 'Domain'."); +DIAGNOSTIC(50052, Error, hullShaderRequiresControlPointWorld, "'HullShader' requires attribute 'ControlPointWorld'.") +DIAGNOSTIC(50052, Error, hullShaderRequiresCornerPointWorld, "'HullShader' requires attribute 'CornerPointWorld'.") +DIAGNOSTIC(50052, Error, hullShaderRequiresDomain, "'HullShader' requires attribute 'Domain'.") DIAGNOSTIC(50052, Error, hullShaderRequiresInputControlPointCount, "'HullShader' requires attribute 'InputControlPointCount'.") DIAGNOSTIC(50052, Error, hullShaderRequiresOutputTopology, "'HullShader' requires attribute 'OutputTopology'.") DIAGNOSTIC(50052, Error, hullShaderRequiresPartitioning, "'HullShader' requires attribute 'Partitioning'.") -DIAGNOSTIC(50052, Error, hullShaderRequiresPatchWorld, "'HullShader' requires attribute 'PatchWorld'."); +DIAGNOSTIC(50052, Error, hullShaderRequiresPatchWorld, "'HullShader' requires attribute 'PatchWorld'.") DIAGNOSTIC(50052, Error, hullShaderRequiresTessLevelInner, "'HullShader' requires attribute 'TessLevelInner'.") DIAGNOSTIC(50052, Error, hullShaderRequiresTessLevelOuter, "'HullShader' requires attribute 'TessLevelOuter'.") -DIAGNOSTIC(50053, Error, invalidTessellationDomian, "'Domain' should be either 'triangles' or 'quads'."); -DIAGNOSTIC(50053, Error, invalidTessellationOutputTopology, "'OutputTopology' must be one of: 'point', 'line', 'triangle_cw', or 'triangle_ccw'."); +DIAGNOSTIC(50053, Error, invalidTessellationDomian, "'Domain' should be either 'triangles' or 'quads'.") +DIAGNOSTIC(50053, Error, invalidTessellationOutputTopology, "'OutputTopology' must be one of: 'point', 'line', 'triangle_cw', or 'triangle_ccw'.") DIAGNOSTIC(50053, Error, invalidTessellationPartitioning, "'Partitioning' must be one of: 'integer', 'pow2', 'fractional_even', or 'fractional_odd'.") DIAGNOSTIC(50053, Error, invalidTessellationDomain, "'Domain' should be either 'triangles' or 'quads'.") DIAGNOSTIC(50082, Error, importingFromPackedBufferUnsupported, "importing type '$0' from PackedBuffer is not supported by the GLSL backend.") DIAGNOSTIC(51090, Error, cannotGenerateCodeForExternComponentType, "cannot generate code for extern component type '$0'.") DIAGNOSTIC(51091, Error, typeCannotBePlacedInATexture, "type '$0' cannot be placed in a texture.") -DIAGNOSTIC(51092, Error, stageDoesntHaveInputWorld, "'$0' doesn't appear to have any input world"); +DIAGNOSTIC(51092, Error, stageDoesntHaveInputWorld, "'$0' doesn't appear to have any input world") -DIAGNOSTIC(52000, Error, multiLevelBreakUnsupported, "control flow appears to require multi-level `break`, which Slang does not yet support"); +DIAGNOSTIC(52000, Error, multiLevelBreakUnsupported, "control flow appears to require multi-level `break`, which Slang does not yet support") -DIAGNOSTIC(52001, Warning, dxilNotFound, "dxil shared library not found, so 'dxc' output cannot be signed! Shader code will not be runnable in non-development environments."); +DIAGNOSTIC(52001, Warning, dxilNotFound, "dxil shared library not found, so 'dxc' output cannot be signed! Shader code will not be runnable in non-development environments.") -DIAGNOSTIC(52002, Error, passThroughCompilerNotFound, "Could not find a suitable pass-through compiler for '$0'."); -DIAGNOSTIC(52003, Error, cppCompilerNotFound, "Could not find a suitable C/C++ compiler for '$0'."); +DIAGNOSTIC(52002, Error, passThroughCompilerNotFound, "Could not find a suitable pass-through compiler for '$0'.") +DIAGNOSTIC(52003, Error, cppCompilerNotFound, "Could not find a suitable C/C++ compiler for '$0'.") -DIAGNOSTIC(52004, Error, unableToWriteFile, "Unable to write file '$0'"); -DIAGNOSTIC(52005, Error, unableToReadFile, "Unable to read file '$0'"); +DIAGNOSTIC(52004, Error, unableToWriteFile, "Unable to write file '$0'") +DIAGNOSTIC(52005, Error, unableToReadFile, "Unable to read file '$0'") // 99999 - Internal compiler errors, and not-yet-classified diagnostics. DIAGNOSTIC(99999, Internal, unimplemented, "unimplemented feature in Slang compiler: $0") DIAGNOSTIC(99999, Internal, unexpected, "unexpected condition encountered in Slang compiler: $0") DIAGNOSTIC(99999, Internal, internalCompilerError, "Slang internal compiler error") -DIAGNOSTIC(99999, Error, compilationAborted, "Slang compilation aborted due to internal error"); -DIAGNOSTIC(99999, Error, compilationAbortedDueToException, "Slang compilation aborted due to an exception of $0: $1"); -DIAGNOSTIC(99999, Note, noteLocationOfInternalError, "the Slang compiler threw an exception while working on code near this location"); -DIAGNOSTIC(99999, Internal, serialDebugVerificationFailed, "Verification of serial debug information failed."); +DIAGNOSTIC(99999, Error, compilationAborted, "Slang compilation aborted due to internal error") +DIAGNOSTIC(99999, Error, compilationAbortedDueToException, "Slang compilation aborted due to an exception of $0: $1") +DIAGNOSTIC(99999, Note, noteLocationOfInternalError, "the Slang compiler threw an exception while working on code near this location") +DIAGNOSTIC(99999, Internal, serialDebugVerificationFailed, "Verification of serial debug information failed.") #undef DIAGNOSTIC diff --git a/source/slang/slang-diagnostics.cpp b/source/slang/slang-diagnostics.cpp index a46e0515b..d16e8e84e 100644 --- a/source/slang/slang-diagnostics.cpp +++ b/source/slang/slang-diagnostics.cpp @@ -3,6 +3,9 @@ #include "slang-name.h" +#include "../core/slang-memory-arena.h" +#include "../core/slang-dictionary.h" + #include <assert.h> #ifdef _WIN32 @@ -268,34 +271,136 @@ void DiagnosticSink::diagnoseRaw( namespace Diagnostics { -#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, messageFormat }; +#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, #name, messageFormat }; #include "slang-diagnostic-defs.h" +#undef DIAGNOSTIC } -DiagnosticInfo const* findDiagnosticByName(UnownedStringSlice const& name) +static const DiagnosticInfo* const kAllDiagnostics[] = { - // TODO: We should eventually have a more formal system for associating individual - // diagnostics, or groups of diagnostics, with user-exposed names for use when - // enabling/disabling warnings (or turning warnings into errors, etc.). - // - // For now we build an ad hoc mapping from string names to corresponding single - // diagnostics (not groups). - // - static const struct +#define DIAGNOSTIC(id, severity, name, messageFormat) &Diagnostics::name, +#include "slang-diagnostic-defs.h" +#undef DIAGNOSTIC +}; + +class DiagnosticsLookup : public RefObject +{ +public: + const DiagnosticInfo* findDiagostic(const UnownedStringSlice& slice) const + { + const Index* indexPtr = m_map.TryGetValue(slice); + return indexPtr ? kAllDiagnostics[*indexPtr] : nullptr; + } + Index _findDiagnosticIndex(const UnownedStringSlice& slice) const { - char const* name; - DiagnosticInfo const* diagnostic; - } kDiagnostics[] = + const Index* indexPtr = m_map.TryGetValue(slice); + return indexPtr ? *indexPtr : 0; + } + static DiagnosticsLookup* getSingleton() + { + static RefPtr<DiagnosticsLookup> singleton = new DiagnosticsLookup; + return singleton; + } + + typedef uint8_t CharFlags; + struct CharFlag { - { "overlapping-bindings", &Diagnostics::parameterBindingsOverlap }, + enum Enum : CharFlags + { + Upper = 0x1, + Lower = 0x2, + }; }; - for( auto& entry : kDiagnostics ) + static CharFlags _classifyChar(char c) + { + CharFlags flags = 0; + flags |= (c >= 'a' && c <= 'z') ? CharFlag::Lower : 0; + flags |= (c >= 'A' && c <= 'Z') ? CharFlag::Upper : 0; + return flags; + } +protected: + void _add(const char* name, Index index) + { + m_map.Add(UnownedStringSlice(name), index); + + // Add a dashed version + { + m_work.Clear(); + + CharFlags prevFlags = 0; + for (const char* cur = name; *cur; cur++) + { + char c = *cur; + const CharFlags flags = _classifyChar(c); + + if (flags & CharFlag::Upper) + { + if (prevFlags & CharFlag::Lower) + { + // If we go from lower to upper, insert a dash. aA -> a-a + m_work << '-'; + } + else if (prevFlags & CharFlag::Upper) + { + // Could be an acronym, if the next character is lower, we need to insert a - here + if (_classifyChar(cur[1]) & CharFlag::Lower) + { + m_work << '-'; + } + } + // Make it lower + c = c - 'A' + 'a'; + } + m_work << c; + + prevFlags = flags; + } + + UnownedStringSlice dashSlice(m_arena.allocateString(m_work.getBuffer(), m_work.getLength()), m_work.getLength()); + m_map.AddIfNotExists(dashSlice, index); + } + } + void _addAlias(const char* name, const char* diagnosticName) + { + const Index index = _findDiagnosticIndex(UnownedStringSlice(diagnosticName)); + SLANG_ASSERT(index >= 0); + if (index >= 0) + { + _add(name, index); + } + } + DiagnosticsLookup(); + + StringBuilder m_work; + Dictionary<UnownedStringSlice, Index> m_map; + MemoryArena m_arena; +}; + +DiagnosticsLookup::DiagnosticsLookup(): + m_arena(2048) +{ + // TODO: We should eventually have a more formal system for associating individual + // diagnostics, or groups of diagnostics, with user-exposed names for use when + // enabling/disabling warnings (or turning warnings into errors, etc.). + // + // For now we build a map from diagnostic name to it's entry. Two entries are typically + // added - the 'original name' as associated with the diagnostic in lowerCamel, and + // a dashified version. + + for (Index i = 0; i < SLANG_COUNT_OF(kAllDiagnostics); ++i) { - if( name == entry.name ) - return entry.diagnostic; + const DiagnosticInfo* diagnostic = kAllDiagnostics[i]; + _add(diagnostic->name, i); } - return nullptr; + + // Add any aliases + _addAlias("overlappingBindings", "parameterBindingsOverlap"); +} + +DiagnosticInfo const* findDiagnosticByName(UnownedStringSlice const& name) +{ + return DiagnosticsLookup::getSingleton()->findDiagostic(name); } diff --git a/source/slang/slang-diagnostics.h b/source/slang/slang-diagnostics.h index 1056cf2eb..caaba4033 100644 --- a/source/slang/slang-diagnostics.h +++ b/source/slang/slang-diagnostics.h @@ -40,6 +40,7 @@ namespace Slang { int id; Severity severity; + char const* name; ///< Unique name char const* messageFormat; }; diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 5279b2a40..7c535d64b 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -15,6 +15,7 @@ #include "slang-type-layout.h" #include "slang-ast-dump.h" +#include "slang-ast-serialize.h" #include "slang-repro.h" @@ -1074,6 +1075,18 @@ void FrontEndCompileRequest::parseTranslationUnit( File::writeAllText(fileName, writer.getContent()); } } + +#if 0 + // Test serialization + { + RefPtr<ASTSerialClasses> classes = new ASTSerialClasses; + ASTSerialWriter writer(classes); + + // Lets serialize it all + writer.addPointer(translationUnit->getModuleDecl()); + } +#endif + } } diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index 3be09765d..5850bc2b8 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -198,6 +198,7 @@ <ClInclude Include="slang-ast-generated-macro.h" /> <ClInclude Include="slang-ast-generated.h" /> <ClInclude Include="slang-ast-modifier.h" /> + <ClInclude Include="slang-ast-serialize.h" /> <ClInclude Include="slang-ast-stmt.h" /> <ClInclude Include="slang-ast-support-types.h" /> <ClInclude Include="slang-ast-type.h" /> @@ -279,6 +280,7 @@ <ClCompile Include="slang-ast-decl.cpp" /> <ClCompile Include="slang-ast-dump.cpp" /> <ClCompile Include="slang-ast-reflect.cpp" /> + <ClCompile Include="slang-ast-serialize.cpp" /> <ClCompile Include="slang-ast-substitutions.cpp" /> <ClCompile Include="slang-ast-type.cpp" /> <ClCompile Include="slang-ast-val.cpp" /> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 64f912110..fdc1bf45d 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -45,6 +45,9 @@ <ClInclude Include="slang-ast-modifier.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="slang-ast-serialize.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="slang-ast-stmt.h"> <Filter>Header Files</Filter> </ClInclude> @@ -284,6 +287,9 @@ <ClCompile Include="slang-ast-reflect.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="slang-ast-serialize.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="slang-ast-substitutions.cpp"> <Filter>Source Files</Filter> </ClCompile> |
