summaryrefslogtreecommitdiff
path: root/source/slang
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/core.meta.slang36
-rw-r--r--source/slang/slang-ast-builder.cpp72
-rw-r--r--source/slang/slang-ast-builder.h65
-rw-r--r--source/slang/slang-ast-expr.h26
-rw-r--r--source/slang/slang-ast-modifier.h40
-rw-r--r--source/slang/slang-ast-type.cpp97
-rw-r--r--source/slang/slang-ast-type.h15
-rw-r--r--source/slang/slang-ast-val.cpp46
-rw-r--r--source/slang/slang-ast-val.h35
-rw-r--r--source/slang/slang-check-conversion.cpp145
-rw-r--r--source/slang/slang-check-expr.cpp49
-rw-r--r--source/slang/slang-check-impl.h9
-rw-r--r--source/slang/slang-emit-c-like.cpp56
-rw-r--r--source/slang/slang-emit-c-like.h16
-rw-r--r--source/slang/slang-emit-hlsl.cpp37
-rw-r--r--source/slang/slang-emit-hlsl.h1
-rw-r--r--source/slang/slang-ir-inst-defs.h3
-rw-r--r--source/slang/slang-ir-insts.h24
-rw-r--r--source/slang/slang-ir.cpp25
-rw-r--r--source/slang/slang-ir.h10
-rw-r--r--source/slang/slang-lower-to-ir.cpp40
-rw-r--r--source/slang/slang-parser.cpp310
22 files changed, 1097 insertions, 60 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index d2f574ef8..ca07b299d 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -27,6 +27,42 @@ syntax globallycoherent : GloballyCoherentModifier;
///
syntax pervertex : PerVertexModifier;
+/// Modifier to indicate a buffer or texture element type is
+/// backed by data in an unsigned normalized format.
+///
+/// The `unorm` modifier is only valid on `float` and `vector`s
+/// with `float` elements.
+///
+/// This modifier does not affect the semantics of any variable,
+/// parameter, or field that uses it. The semantics of a `float`
+/// or vector are the same with or without `unorm`.
+///
+/// The `unorm` modifier can be used for the element type of a
+/// buffer or texture, to indicate that the data that is bound
+/// to that buffer or texture is in a matching normalized format.
+/// Some platforms may require a `unorm` qualifier for such buffers
+/// and textures, and others may operate correctly without it.
+///
+syntax unorm : UNormModifier;
+
+/// Modifier to indicate a buffer or texture element type is
+/// backed by data in an signed normalized format.
+///
+/// The `snorm` modifier is only valid on `float` and `vector`s
+/// with `float` elements.
+///
+/// This modifier does not affect the semantics of any variable,
+/// parameter, or field that uses it. The semantics of a `float`
+/// or vector are the same with or without `snorm`.
+///
+/// The `snorm` modifier can be used for the element type of a
+/// buffer or texture, to indicate that the data that is bound
+/// to that buffer or texture is in a matching normalized format.
+/// Some platforms may require a `unorm` qualifier for such buffers
+/// and textures, and others may operate correctly without it.
+///
+syntax snorm : SNormModifier;
+
/// A type that can be used as an operand for builtins
[sealed]
[builtin]
diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp
index 4948a68a6..8e1813df3 100644
--- a/source/slang/slang-ast-builder.cpp
+++ b/source/slang/slang-ast-builder.cpp
@@ -288,10 +288,82 @@ Type* ASTBuilder::getAndType(Type* left, Type* right)
return type;
}
+Type* ASTBuilder::getModifiedType(Type* base, Count modifierCount, Val* const* modifiers)
+{
+ auto type = create<ModifiedType>();
+ type->base = base;
+ type->modifiers.addRange(modifiers, modifierCount);
+ return type;
+}
+
+NodeBase* ASTBuilder::_getOrCreateImpl(NodeDesc const& desc, NodeCreateFunc createFunc, void* createFuncUserData)
+{
+ if(auto found = m_cachedNodes.TryGetValue(desc))
+ return *found;
+
+ auto node = createFunc(this, desc, createFuncUserData);
+
+ auto operandCount = desc.operandCount;
+ NodeBase** operandsCopy = m_arena.allocateAndZeroArray<NodeBase*>(desc.operandCount);
+ for(Index i = 0; i < operandCount; ++i)
+ operandsCopy[i] = desc.operands[i];
+
+ NodeDesc descCopy = desc;
+ descCopy.operands = operandsCopy;
+ m_cachedNodes.Add(descCopy, node);
+
+ return node;
+}
+
+
+Val* ASTBuilder::getUNormModifierVal()
+{
+ return _getOrCreate<UNormModifierVal>();
+}
+
+Val* ASTBuilder::getSNormModifierVal()
+{
+ return _getOrCreate<SNormModifierVal>();
+}
+
TypeType* ASTBuilder::getTypeType(Type* type)
{
return create<TypeType>(type);
}
+bool ASTBuilder::NodeDesc::operator==(NodeDesc const& that) const
+{
+ if(type != that.type) return false;
+ if(operandCount != that.operandCount) return false;
+ for(Index i = 0; i < operandCount; ++i)
+ {
+ // Note: we are comparing the operands directly for identity
+ // (pointer equality) rather than doing the `Val`-level
+ // equality check.
+ //
+ // The rationale here is that nodes that will be created
+ // via a `NodeDesc` *should* all be going through the
+ // deduplication path anyway, as should their operands.
+ //
+ if(operands[i] != that.operands[i]) return false;
+ }
+ return true;
+}
+HashCode ASTBuilder::NodeDesc::getHashCode() const
+{
+ Hasher hasher;
+ hasher.hashValue(Int(type));
+ hasher.hashValue(operandCount);
+ for(Index i = 0; i < operandCount; ++i)
+ {
+ // Note: we are hashing the raw pointer value rather
+ // than the content of the value node. This is done
+ // to match the semantics implemented for `==` on
+ // `NodeDesc`.
+ //
+ hasher.hashValue((void*) operands[i]);
+ }
+ return hasher.getResult();
+}
} // namespace Slang
diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h
index 2838230bd..7af5ae710 100644
--- a/source/slang/slang-ast-builder.h
+++ b/source/slang/slang-ast-builder.h
@@ -157,6 +157,14 @@ public:
Type* getAndType(Type* left, Type* right);
+ Type* getModifiedType(Type* base, Count modifierCount, Val* const* modifiers);
+ Type* getModifiedType(Type* base, List<Val*> const& modifiers)
+ {
+ return getModifiedType(base, modifiers.getCount(), modifiers.getBuffer());
+ }
+ Val* getUNormModifierVal();
+ Val* getSNormModifierVal();
+
TypeType* getTypeType(Type* type);
/// Helpers to get type info from the SharedASTBuilder
@@ -208,6 +216,63 @@ protected:
SharedASTBuilder* m_sharedASTBuilder;
MemoryArena m_arena;
+
+ struct NodeDesc
+ {
+ ASTNodeType type;
+ Count operandCount = 0;
+ NodeBase* const* operands = nullptr;
+
+ bool operator==(NodeDesc const& that) const;
+ HashCode getHashCode() const;
+ };
+
+ /// A cache for AST nodes that are entirely defined by their node type, with
+ /// no need for additional state.
+ Dictionary<NodeDesc, NodeBase*> m_cachedNodes;
+
+
+ typedef NodeBase* (*NodeCreateFunc)(ASTBuilder* astBuilder, NodeDesc const& desc, void* userData);
+
+ NodeBase* _getOrCreateImpl(NodeDesc const& desc, NodeCreateFunc createFunc, void* createFuncUserData);
+
+ template<typename T>
+ SLANG_FORCE_INLINE T* _getOrCreate(Count operandCount, NodeBase* const* operands)
+ {
+ SLANG_COMPILE_TIME_ASSERT(IsValidType<T>::Value);
+
+ struct Helper
+ {
+ static NodeBase* create(ASTBuilder* astBuilder, NodeDesc const& desc, void* /*userData*/)
+ {
+ return astBuilder->create<T>(desc.operandCount, desc.operands);
+ }
+ };
+
+ NodeDesc desc;
+ desc.type = T::kType;
+ desc.operandCount = operandCount;
+ desc.operands = operands;
+ return (T*) _getOrCreateImpl(desc, &Helper::create, nullptr);
+ }
+
+ template<typename T>
+ SLANG_FORCE_INLINE T* _getOrCreate()
+ {
+ SLANG_COMPILE_TIME_ASSERT(IsValidType<T>::Value);
+
+ struct Helper
+ {
+ static NodeBase* create(ASTBuilder* astBuilder, NodeDesc const& /*desc*/, void* /*userData*/)
+ {
+ return astBuilder->create<T>();
+ }
+ };
+
+ NodeDesc desc;
+ desc.type = T::kType;
+ return (T*) _getOrCreateImpl(desc, &Helper::create, nullptr);
+ }
};
} // namespace Slang
diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h
index 81bed9264..b73e07042 100644
--- a/source/slang/slang-ast-expr.h
+++ b/source/slang/slang-ast-expr.h
@@ -240,7 +240,7 @@ class CastToSuperTypeExpr: public Expr
/// The value being cast to a super type
///
- /// The type being case from is `valueArg->type`.
+ /// The type being cast from is `valueArg->type`.
///
Expr* valueArg = nullptr;
@@ -248,6 +248,21 @@ class CastToSuperTypeExpr: public Expr
Val* witnessArg = nullptr;
};
+ /// A cast of a value to the same type, with different modifiers.
+ ///
+ /// The type being cast to is stored as this expression's `type`.
+ ///
+class ModifierCastExpr : public Expr
+{
+ SLANG_AST_CLASS(ModifierCastExpr)
+
+ /// The value being cast.
+ ///
+ /// The type being cast from is `valueArg->type`.
+ ///
+ Expr* valueArg = nullptr;
+};
+
class SelectExpr: public OperatorExpr
{
SLANG_AST_CLASS(SelectExpr)
@@ -340,4 +355,13 @@ class AndTypeExpr : public Expr
TypeExp right;
};
+ /// A type exprssion that applies one or more modifiers to another type
+class ModifiedTypeExpr : public Expr
+{
+ SLANG_AST_CLASS(ModifiedTypeExpr);
+
+ Modifiers modifiers;
+ TypeExp base;
+};
+
} // namespace Slang
diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h
index 2929a53c2..2558d7c4b 100644
--- a/source/slang/slang-ast-modifier.h
+++ b/source/slang/slang-ast-modifier.h
@@ -983,4 +983,44 @@ class PayloadAttribute : public Attribute
SLANG_AST_CLASS(PayloadAttribute)
};
+ /// A modifier that applies to types rather than declarations.
+ ///
+ /// In most cases, the Slang compiler assumes that a modifier should
+ /// inhere to a declaration. Given input like:
+ ///
+ /// mod1 mod2 int myVar = ...;
+ ///
+ /// The default assumption is that `mod1` and `mod2` apply to `myVar`
+ /// and *not* to the `int` type.
+ ///
+ /// In order to allow modifiers to inhere to the type instead, we introduce
+ /// a base class for modifiers that really don't want to belong to the declaration,
+ /// and instead want to belong to the type (or rather the type *specifier*
+ /// from a parsing standpoint).
+ ///
+class TypeModifier : public Modifier
+{
+ SLANG_AST_CLASS(TypeModifier)
+};
+
+ /// A modifier that applies to a type and implies information about the
+ /// underlying format of a resource that uses that type as its element type.
+ ///
+class ResourceElementFormatModifier : public TypeModifier
+{
+ SLANG_AST_CLASS(ResourceElementFormatModifier)
+};
+
+ /// HLSL `unorm` modifier
+class UNormModifier : public ResourceElementFormatModifier
+{
+ SLANG_AST_CLASS(UNormModifier)
+};
+
+ /// HLSL `snorm` modifier
+class SNormModifier : public ResourceElementFormatModifier
+{
+ SLANG_AST_CLASS(SNormModifier)
+};
+
} // namespace Slang
diff --git a/source/slang/slang-ast-type.cpp b/source/slang/slang-ast-type.cpp
index 45a736f23..b6bc1f170 100644
--- a/source/slang/slang-ast-type.cpp
+++ b/source/slang/slang-ast-type.cpp
@@ -1053,5 +1053,102 @@ Val* AndType::_substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet su
return substType;
}
+// ModifiedType
+
+
+void ModifiedType::_toTextOverride(StringBuilder& out)
+{
+ for( auto modifier : modifiers )
+ {
+ modifier->toText(out);
+ out.appendChar(' ');
+ }
+ base->toText(out);
+}
+
+bool ModifiedType::_equalsImplOverride(Type* type)
+{
+ auto other = as<ModifiedType>(type);
+ if(!other)
+ return false;
+
+ if(!base->equals(other->base))
+ return false;
+
+ // TODO: Eventually we need to put the `modifiers` into
+ // a canonical ordering as part of creation of a `ModifiedType`,
+ // so that two instances that apply the same modifiers to
+ // the same type will have those modifiers in a matching order.
+ //
+ // The simplest way to achieve that ordering *for now* would
+ // be to sort the array by the integer AST node type tag.
+ // That approach would of course not scale to modifiers that
+ // have any operands of their own.
+ //
+ // Note that we would *also* need the logic that creates a
+ // `ModifiedType` to detect when the base type is itself a
+ // `ModifiedType` and produce a single `ModifiedType` with
+ // a combined list of modifiers and a non-`ModifiedType` as
+ // its base type.
+ //
+ auto modifierCount = modifiers.getCount();
+ if(modifierCount != other->modifiers.getCount())
+ return false;
+
+ for( Index i = 0; i < modifierCount; ++i )
+ {
+ auto thisModifier = this->modifiers[i];
+ auto otherModifier = other->modifiers[i];
+ if(!thisModifier->equalsVal(otherModifier))
+ return false;
+ }
+ return true;
+}
+
+HashCode ModifiedType::_getHashCodeOverride()
+{
+ Hasher hasher;
+ hasher.hashObject(base);
+ for( auto modifier : modifiers )
+ {
+ hasher.hashObject(modifier);
+ }
+ return hasher.getResult();
+}
+
+Type* ModifiedType::_createCanonicalTypeOverride()
+{
+ ModifiedType* canonical = m_astBuilder->create<ModifiedType>();
+ canonical->base = base->getCanonicalType();
+ for( auto modifier : modifiers )
+ {
+ canonical->modifiers.add(modifier);
+ }
+ return canonical;
+}
+
+Val* ModifiedType::_substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff)
+{
+ int diff = 0;
+ Type* substBase = as<Type>(base->substituteImpl(astBuilder, subst, &diff));
+
+ List<Val*> substModifiers;
+ for( auto modifier : modifiers )
+ {
+ auto substModifier = modifier->substituteImpl(astBuilder, subst, &diff);
+ substModifiers.add(substModifier);
+ }
+
+ if(!diff)
+ return this;
+
+ *ioDiff = 1;
+
+ ModifiedType* substType = m_astBuilder->create<ModifiedType>();
+ substType->base = substBase;
+ substType->modifiers = _Move(substModifiers);
+ return substType;
+}
+
} // namespace Slang
diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h
index 5b44a5087..f919e7bc2 100644
--- a/source/slang/slang-ast-type.h
+++ b/source/slang/slang-ast-type.h
@@ -729,4 +729,19 @@ class AndType : public Type
Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff);
};
+class ModifiedType : public Type
+{
+ SLANG_AST_CLASS(ModifiedType)
+
+ Type* base;
+ List<Val*> modifiers;
+
+ // Overrides should be public so base classes can access
+ void _toTextOverride(StringBuilder& out);
+ bool _equalsImplOverride(Type* type);
+ HashCode _getHashCodeOverride();
+ Type* _createCanonicalTypeOverride();
+ Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff);
+};
+
} // namespace Slang
diff --git a/source/slang/slang-ast-val.cpp b/source/slang/slang-ast-val.cpp
index 97d79e290..0f3bd2b3a 100644
--- a/source/slang/slang-ast-val.cpp
+++ b/source/slang/slang-ast-val.cpp
@@ -528,6 +528,52 @@ Val* TaggedUnionSubtypeWitness::_substituteImplOverride(ASTBuilder* astBuilder,
return substWitness;
}
+// ModifierVal
+bool ModifierVal::_equalsValOverride(Val* val)
+{
+ // TODO: This is assuming we can fully deduplicate the values that represent
+ // modifiers, which may not actually be the case if there are multiple modules
+ // being combined that use different `ASTBuilder`s.
+ //
+ return this == val;
+}
+
+HashCode ModifierVal::_getHashCodeOverride()
+{
+ Hasher hasher;
+ hasher.hashValue((void*) this);
+ return hasher.getResult();
+}
+
+// UNormModifierVal
+
+void UNormModifierVal::_toTextOverride(StringBuilder& out)
+{
+ out.append("unorm");
+}
+
+Val* UNormModifierVal::_substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff)
+{
+ SLANG_UNUSED(astBuilder);
+ SLANG_UNUSED(subst);
+ SLANG_UNUSED(ioDiff);
+ return this;
+}
+
+// SNormModifierVal
+
+void SNormModifierVal::_toTextOverride(StringBuilder& out)
+{
+ out.append("snorm");
+}
+
+Val* SNormModifierVal::_substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff)
+{
+ SLANG_UNUSED(astBuilder);
+ SLANG_UNUSED(subst);
+ SLANG_UNUSED(ioDiff);
+ return this;
+}
} // namespace Slang
diff --git a/source/slang/slang-ast-val.h b/source/slang/slang-ast-val.h
index b76d6325f..5fd3e54f5 100644
--- a/source/slang/slang-ast-val.h
+++ b/source/slang/slang-ast-val.h
@@ -241,4 +241,39 @@ class ExtractFromConjunctionSubtypeWitness : public SubtypeWitness
int indexInConjunction;
};
+ /// A value that represents a modifier attached to some other value
+class ModifierVal : public Val
+{
+ SLANG_AST_CLASS(ModifierVal)
+
+ bool _equalsValOverride(Val* val);
+ HashCode _getHashCodeOverride();
+};
+
+class TypeModifierVal : public ModifierVal
+{
+ SLANG_AST_CLASS(TypeModifierVal)
+};
+
+class ResourceFormatModifierVal : public TypeModifierVal
+{
+ SLANG_AST_CLASS(ResourceFormatModifierVal)
+};
+
+class UNormModifierVal : public ResourceFormatModifierVal
+{
+ SLANG_AST_CLASS(UNormModifierVal)
+
+ void _toTextOverride(StringBuilder& out);
+ Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff);
+};
+
+class SNormModifierVal : public ResourceFormatModifierVal
+{
+ SLANG_AST_CLASS(SNormModifierVal)
+
+ void _toTextOverride(StringBuilder& out);
+ Val* _substituteImplOverride(ASTBuilder* astBuilder, SubstitutionSet subst, int* ioDiff);
+};
+
} // namespace Slang
diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp
index b6c7069a2..855590472 100644
--- a/source/slang/slang-check-conversion.cpp
+++ b/source/slang/slang-check-conversion.cpp
@@ -539,6 +539,68 @@ namespace Slang
return false;
}
+ /// Do the `left` and `right` modifiers represent the same thing?
+ static bool _doModifiersMatch(Val* left, Val* right)
+ {
+ if( left == right )
+ return true;
+
+ if( left->equalsVal(right) )
+ return true;
+
+ return false;
+ }
+
+ /// Does `type` have a modifier that matches `modifier`?
+ static bool _hasMatchingModifier(ModifiedType* type, Val* modifier)
+ {
+ if(!type) return false;
+
+ for( auto m : type->modifiers )
+ {
+ if(_doModifiersMatch(m, modifier))
+ return true;
+ }
+
+ return false;
+ }
+
+ /// Can `modifier` be added to a type as part of a coercion?
+ ///
+ /// For example, it is generally safe to convert from a value
+ /// of type `T` to a value of type `const T` in C/C++.
+ ///
+ static bool _canModifierBeAddedDuringCoercion(Val* modifier)
+ {
+ switch( modifier->astNodeType )
+ {
+ default:
+ return false;
+
+ case ASTNodeType::UNormModifierVal:
+ case ASTNodeType::SNormModifierVal:
+ return true;
+ }
+ }
+
+ /// Can `modifier` be dropped from a type as part of a coercion?
+ ///
+ /// For example, it is generally safe to convert from a value
+ /// of type `const T` to a value of type `T` in C/C++.
+ ///
+ static bool _canModifierBeDroppedDuringCoercion(Val* modifier)
+ {
+ switch( modifier->astNodeType )
+ {
+ default:
+ return false;
+
+ case ASTNodeType::UNormModifierVal:
+ case ASTNodeType::SNormModifierVal:
+ return true;
+ }
+ }
+
bool SemanticsVisitor::_coerce(
Type* toType,
Expr** outToExpr,
@@ -592,6 +654,77 @@ namespace Slang
return true;
}
+ {
+ // It is possible that one or more of the types involved might have modifiers
+ // on it, but the underlying types are otherwise the same.
+ //
+ auto toModified = as<ModifiedType>(toType);
+ auto toBase = toModified ? toModified->base : toType;
+ //
+ auto fromModified = as<ModifiedType>(fromType);
+ auto fromBase = fromModified ? fromModified->base : fromType;
+
+
+ if((toModified || fromModified) && toBase->equals(fromBase))
+ {
+ // We need to check each modifier present on either `toType`
+ // or `fromType`. For each modifier, it will either be:
+ //
+ // * Present on both types; these are a non-issue
+ // * Present only on `toType`
+ // * Present only on `fromType`
+ //
+ if( toModified )
+ {
+ for( auto modifier : toModified->modifiers )
+ {
+ if(_hasMatchingModifier(fromModified, modifier))
+ continue;
+
+ // If `modifier` is present on `toType`, but not `fromType`,
+ // then we need to know whether this modifier can be added
+ // to the type of an expression as part of coercion.
+ //
+ if( !_canModifierBeAddedDuringCoercion(modifier) )
+ {
+ return _failedCoercion(toType, outToExpr, fromExpr);
+ }
+ }
+ }
+ if( fromModified )
+ {
+ for( auto modifier : fromModified->modifiers )
+ {
+ if(_hasMatchingModifier(toModified, modifier))
+ continue;
+
+ // If `modifier` is present on `fromType`, but not `toType`,
+ // then we need to know whether this modifier can be dropped
+ // to the type of an expression as part of coercion.
+ //
+ if( !_canModifierBeDroppedDuringCoercion(modifier) )
+ {
+ return _failedCoercion(toType, outToExpr, fromExpr);
+ }
+ }
+ }
+
+ // If all the modifiers were okay, we can convert.
+
+ // TODO: we may need a cost to allow disambiguation of overloads based on modifiers?
+ if(outCost)
+ {
+ *outCost = kConversionCost_None;
+ }
+ if( outToExpr )
+ {
+ *outToExpr = createModifierCastExpr(toType, fromExpr);
+ }
+
+ return true;
+ }
+ }
+
// Coercion from an initializer list is allowed for many types,
// so we will farm that out to its own subroutine.
//
@@ -931,6 +1064,18 @@ namespace Slang
return expr;
}
+ Expr* SemanticsVisitor::createModifierCastExpr(
+ Type* toType,
+ Expr* fromExpr)
+ {
+ ModifierCastExpr* expr = m_astBuilder->create<ModifierCastExpr>();
+ expr->loc = fromExpr->loc;
+ expr->type = QualType(toType);
+ expr->valueArg = fromExpr;
+ return expr;
+ }
+
+
Expr* SemanticsVisitor::coerce(
Type* toType,
Expr* fromExpr)
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index e8384e0ab..906a21d53 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -2142,4 +2142,53 @@ namespace Slang
return expr;
}
+ Expr* SemanticsExprVisitor::visitModifiedTypeExpr(ModifiedTypeExpr* expr)
+ {
+ // The base type should be a proper type (not an expression, generic, etc.)
+ //
+ expr->base = CheckProperType(expr->base);
+ auto baseType = expr->base.type;
+
+ // We will check the modifiers that were applied to the type expression
+ // one by one, and collect a list of the ones that should modify the
+ // resulting `Type`.
+ //
+ List<Val*> modifierVals;
+ for( auto modifier : expr->modifiers )
+ {
+ auto modifierVal = checkTypeModifier(modifier, baseType);
+ if(!modifierVal)
+ continue;
+ modifierVals.add(modifierVal);
+ }
+
+ auto modifiedType = m_astBuilder->getModifiedType(baseType, modifierVals);
+ expr->type = m_astBuilder->getTypeType(modifiedType);
+
+ return expr;
+ }
+
+ Val* SemanticsExprVisitor::checkTypeModifier(Modifier* modifier, Type* type)
+ {
+ SLANG_UNUSED(type);
+
+ if( auto unormModifier = as<UNormModifier>(modifier) )
+ {
+ // TODO: validate that `type` is either `float` or a vector of `float`s
+ return m_astBuilder->getUNormModifierVal();
+
+ }
+ else if( auto snormModifier = as<SNormModifier>(modifier) )
+ {
+ // TODO: validate that `type` is either `float` or a vector of `float`s
+ return m_astBuilder->getSNormModifierVal();
+ }
+ else
+ {
+ // TODO: more complete error message here
+ getSink()->diagnose(modifier, Diagnostics::unexpected, "unknown type modifier in semantic checking");
+ return nullptr;
+ }
+ }
+
}
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index bd2392c67..8ea693104 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -1154,6 +1154,10 @@ namespace Slang
Expr* fromExpr,
Val* witness);
+ Expr* createModifierCastExpr(
+ Type* toType,
+ Expr* fromExpr);
+
/// Does there exist an implicit conversion from `fromType` to `toType`?
bool canConvertImplicitly(
Type* toType,
@@ -1573,6 +1577,7 @@ namespace Slang
CASE(OverloadedExpr2)
CASE(AggTypeCtorExpr)
CASE(CastToSuperTypeExpr)
+ CASE(ModifierCastExpr)
CASE(LetExpr)
CASE(ExtractExistentialValueExpr)
@@ -1587,6 +1592,10 @@ namespace Slang
Expr* visitThisExpr(ThisExpr* expr);
Expr* visitThisTypeExpr(ThisTypeExpr* expr);
Expr* visitAndTypeExpr(AndTypeExpr* expr);
+ Expr* visitModifiedTypeExpr(ModifiedTypeExpr* expr);
+
+ /// Perform semantic checking on a `modifier` that is being applied to the given `type`
+ Val* checkTypeModifier(Modifier* modifier, Type* type);
};
struct SemanticsStmtVisitor
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index 06d34c260..e04759773 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -170,6 +170,18 @@ void CLikeSourceEmitter::emitDeclarator(DeclaratorInfo* declarator)
}
break;
+ case DeclaratorInfo::Flavor::Attributed:
+ {
+ auto attributedDeclarator = (AttributedDeclaratorInfo*)declarator;
+ auto instWithAttributes = attributedDeclarator->instWithAttributes;
+ for(auto attr : instWithAttributes->getAllAttrs())
+ {
+ _emitPostfixTypeAttr(attr);
+ }
+ emitDeclarator(attributedDeclarator->next);
+ }
+ break;
+
default:
SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unknown declarator flavor");
@@ -249,16 +261,24 @@ List<IRWitnessTableEntry*> CLikeSourceEmitter::getSortedWitnessTableEntries(IRWi
return sortedWitnessTableEntries;
}
-void CLikeSourceEmitter::_emitArrayType(IRArrayType* arrayType, DeclaratorInfo* declarator)
+void CLikeSourceEmitter::_emitPrefixTypeAttr(IRAttr* attr)
{
- SizedArrayDeclaratorInfo arrayDeclarator(declarator, arrayType->getElementCount());
- _emitType(arrayType->getElementType(), &arrayDeclarator);
+ SLANG_UNUSED(attr);
+
+ // By defualt we will not emit any attributes.
+ //
+ // TODO: If `const` ever surfaces as a type attribute in our IR,
+ // we may need to handle it here.
}
-void CLikeSourceEmitter::_emitUnsizedArrayType(IRUnsizedArrayType* arrayType, DeclaratorInfo* declarator)
+void CLikeSourceEmitter::_emitPostfixTypeAttr(IRAttr* attr)
{
- UnsizedArrayDeclaratorInfo arrayDeclarator(declarator);
- _emitType(arrayType->getElementType(), &arrayDeclarator);
+ SLANG_UNUSED(attr);
+
+ // By defualt we will not emit any attributes.
+ //
+ // TODO: If `const` ever surfaces as a type attribute in our IR,
+ // we may need to handle it here.
}
void CLikeSourceEmitter::_emitType(IRType* type, DeclaratorInfo* declarator)
@@ -278,11 +298,31 @@ void CLikeSourceEmitter::_emitType(IRType* type, DeclaratorInfo* declarator)
break;
case kIROp_ArrayType:
- _emitArrayType(cast<IRArrayType>(type), declarator);
+ {
+ auto arrayType = cast<IRArrayType>(type);
+ SizedArrayDeclaratorInfo arrayDeclarator(declarator, arrayType->getElementCount());
+ _emitType(arrayType->getElementType(), &arrayDeclarator);
+ }
break;
case kIROp_UnsizedArrayType:
- _emitUnsizedArrayType(cast<IRUnsizedArrayType>(type), declarator);
+ {
+ auto arrayType = cast<IRUnsizedArrayType>(type);
+ UnsizedArrayDeclaratorInfo arrayDeclarator(declarator);
+ _emitType(arrayType->getElementType(), &arrayDeclarator);
+ }
+ break;
+
+ case kIROp_AttributedType:
+ {
+ auto attributedType = cast<IRAttributedType>(type);
+ for(auto attr : attributedType->getAllAttrs())
+ {
+ _emitPrefixTypeAttr(attr);
+ }
+ AttributedDeclaratorInfo attributedDeclarator(declarator, attributedType);
+ _emitType(attributedType->getBaseType(), &attributedDeclarator);
+ }
break;
}
diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h
index 08d24ef04..ed0f69a2c 100644
--- a/source/slang/slang-emit-c-like.h
+++ b/source/slang/slang-emit-c-like.h
@@ -80,6 +80,7 @@ public:
SizedArray,
UnsizedArray,
LiteralSizedArray,
+ Attributed,
};
Flavor flavor;
@@ -145,6 +146,16 @@ public:
{}
};
+ struct AttributedDeclaratorInfo : ChainedDeclaratorInfo
+ {
+ AttributedDeclaratorInfo(DeclaratorInfo* next, IRInst* instWithAttributes)
+ : ChainedDeclaratorInfo(Flavor::Attributed, next)
+ , instWithAttributes(instWithAttributes)
+ {}
+
+ IRInst* instWithAttributes;
+ };
+
struct ComputeEmitActionsContext;
// An action to be performed during code emit.
@@ -449,11 +460,12 @@ public:
virtual void emitPostKeywordTypeAttributesImpl(IRInst* inst) { SLANG_UNUSED(inst); }
- void _emitArrayType(IRArrayType* arrayType, DeclaratorInfo* declarator);
- void _emitUnsizedArrayType(IRUnsizedArrayType* arrayType, DeclaratorInfo* declarator);
void _emitType(IRType* type, DeclaratorInfo* declarator);
void _emitInst(IRInst* inst);
+ virtual void _emitPrefixTypeAttr(IRAttr* attr);
+ virtual void _emitPostfixTypeAttr(IRAttr* attr);
+
// Emit the argument list (including paranthesis) in a `CallInst`
void _emitCallArgList(IRCall* call);
diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp
index dd5c18315..09749546a 100644
--- a/source/slang/slang-emit-hlsl.cpp
+++ b/source/slang/slang-emit-hlsl.cpp
@@ -681,7 +681,30 @@ void HLSLSourceEmitter::emitLayoutDirectivesImpl(TargetRequest* targetReq)
void HLSLSourceEmitter::emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount)
{
- // TODO(tfoley) : should really emit these with sugar
+ // In some cases we *need* to use the built-in syntax sugar for vector types,
+ // so we will try to emit those whenever possible.
+ //
+ if( elementCount >= 1 && elementCount <= 4 )
+ {
+ switch( elementType->getOp() )
+ {
+ case kIROp_FloatType:
+ case kIROp_IntType:
+ case kIROp_UIntType:
+ // TODO: There are more types that need to be covered here
+ emitType(elementType);
+ m_writer->emit(elementCount);
+ return;
+
+ default:
+ break;
+ }
+ }
+
+ // As a fallback, we will use the `vector<...>` type constructor,
+ // although we should not expect to run into types that don't
+ // have a sugared form.
+ //
m_writer->emit("vector<");
emitType(elementType);
m_writer->emit(",");
@@ -973,6 +996,18 @@ void HLSLSourceEmitter::emitPostKeywordTypeAttributesImpl(IRInst* inst)
}
}
+void HLSLSourceEmitter::_emitPrefixTypeAttr(IRAttr* attr)
+{
+ switch( attr->getOp() )
+ {
+ default:
+ Super::_emitPrefixTypeAttr(attr);
+ break;
+
+ case kIROp_UNormAttr: m_writer->emit("unorm "); break;
+ case kIROp_SNormAttr: m_writer->emit("snorm "); break;
+ }
+}
void HLSLSourceEmitter::emitSimpleFuncParamImpl(IRParam* param)
{
diff --git a/source/slang/slang-emit-hlsl.h b/source/slang/slang-emit-hlsl.h
index 59449e519..eeda7c0b7 100644
--- a/source/slang/slang-emit-hlsl.h
+++ b/source/slang/slang-emit-hlsl.h
@@ -55,6 +55,7 @@ protected:
virtual void emitPostKeywordTypeAttributesImpl(IRInst* inst) SLANG_OVERRIDE;
+ void _emitPrefixTypeAttr(IRAttr* attr) SLANG_OVERRIDE;
// Emit a single `register` semantic, as appropriate for a given resource-type-specific layout info
// Keyword to use in the uniform case (`register` for globals, `packoffset` inside a `cbuffer`)
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index 8e8a243a0..9fa5a2e9d 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -52,6 +52,7 @@ INST(Nop, nop, 0, 0)
INST(TaggedUnionType, TaggedUnion, 0, 0)
INST(ConjunctionType, Conjunction, 0, 0)
+ INST(AttributedType, Attributed, 0, 0)
/* BindExistentialsTypeBase */
@@ -695,6 +696,8 @@ INST_RANGE(Layout, VarLayout, EntryPointLayout)
INST(StageAttr, stage, 1, 0)
INST(StructFieldLayoutAttr, fieldLayout, 2, 0)
INST(CaseTypeLayoutAttr, caseLayout, 1, 0)
+ INST(UNormAttr, unorm, 0, 0)
+ INST(SNormAttr, snorm, 0, 0)
/* SemanticAttr */
INST(UserSemanticAttr, userSemantic, 2, 0)
INST(SystemValueSemanticAttr, systemValueSemantic, 2, 0)
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index cbcefa56a..7ef31d71a 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -2113,6 +2113,18 @@ public:
return getConjunctionType(2, types);
}
+ IRType* getAttributedType(
+ IRType* baseType,
+ UInt attributeCount,
+ IRAttr* const* attributes);
+
+ IRType* getAttributedType(
+ IRType* baseType,
+ List<IRAttr*> attributes)
+ {
+ return getAttributedType(baseType, attributes.getCount(), attributes.getBuffer());
+ }
+
// Set the data type of an instruction, while preserving
// its rate, if any.
void setDataType(IRInst* inst, IRType* dataType);
@@ -2643,6 +2655,18 @@ public:
IRStageAttr* getStageAttr(Stage stage);
+ IRAttr* getAttr(IROp op, UInt operandCount, IRInst* const* operands);
+
+ IRAttr* getAttr(IROp op, List<IRInst*> const& operands)
+ {
+ return getAttr(op, operands.getCount(), operands.getBuffer());
+ }
+
+ IRAttr* getAttr(IROp op)
+ {
+ return getAttr(op, 0, nullptr);
+ }
+
IRTypeLayout* getTypeLayout(IROp op, List<IRInst*> const& operands);
IRVarLayout* getVarLayout(List<IRInst*> const& operands);
IREntryPointLayout* getEntryPointLayout(
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index 27eb1adfb..f7ebfdb64 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -200,7 +200,7 @@ namespace Slang
return nullptr;
}
- IROperandListBase IRInst::getAllAttrs()
+ IROperandList<IRAttr> IRInst::getAllAttrs()
{
// We assume as an invariant that all attributes appear at the end of the operand
// list, after all the non-attribute operands.
@@ -215,7 +215,7 @@ namespace Slang
while(cursor != end && !as<IRAttr>(cursor->get()))
cursor++;
- return IROperandListBase(cursor, end);
+ return IROperandList<IRAttr>(cursor, end);
}
// IRConstant
@@ -2766,6 +2766,17 @@ namespace Slang
return getType(kIROp_ConjunctionType, typeCount, (IRInst* const*)types);
}
+ IRType* IRBuilder::getAttributedType(
+ IRType* baseType,
+ UInt attributeCount,
+ IRAttr* const* attributes)
+ {
+ List<IRInst*> operands;
+ operands.add(baseType);
+ for(UInt i = 0; i < attributeCount; ++i)
+ operands.add(attributes[i]);
+ return getType(kIROp_AttributedType, operands.getCount(), operands.getBuffer());
+ }
void IRBuilder::setDataType(IRInst* inst, IRType* dataType)
@@ -4258,6 +4269,16 @@ namespace Slang
operands));
}
+ IRAttr* IRBuilder::getAttr(IROp op, UInt operandCount, IRInst* const* operands)
+ {
+ return cast<IRAttr>(findOrEmitHoistableInst(
+ getVoidType(),
+ op,
+ operandCount,
+ operands));
+ }
+
+
IRTypeLayout* IRBuilder::getTypeLayout(IROp op, List<IRInst*> const& operands)
{
diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h
index 4d1c153b4..04fd6b1c9 100644
--- a/source/slang/slang-ir.h
+++ b/source/slang/slang-ir.h
@@ -125,6 +125,7 @@ struct IRBlock;
struct IRDecoration;
struct IRRate;
struct IRType;
+struct IRAttr;
// A double-linked list of instruction
struct IRInstListBase
@@ -492,7 +493,7 @@ struct IRInst
T* findDecoration();
/// Get all the attributes attached to this instruction.
- IROperandListBase getAllAttrs();
+ IROperandList<IRAttr> getAllAttrs();
/// Find the first attribute of type `T` attached to this instruction.
template<typename T>
@@ -1428,6 +1429,13 @@ struct IRConjunctionType : IRType
IRType* getCaseType(Int index) { return (IRType*) getOperand(index); }
};
+struct IRAttributedType : IRType
+{
+ IR_LEAF_ISA(AttributedType)
+
+ IRType* getBaseType() { return (IRType*) getOperand(0); }
+};
+
/// Represents a tuple. Tuples are created by `IRMakeTuple` and its elements
/// are accessed via `GetTupleElement(tupleValue, IRIntLit)`.
struct IRTupleType : IRType
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 40c88a0bc..b47448ae1 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -1777,6 +1777,34 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower
return LoweredValInfo::simple(irType);
}
+ LoweredValInfo visitModifiedType(ModifiedType* astType)
+ {
+ IRType* irBase = lowerType(context, astType->base);
+
+ List<IRAttr*> irAttrs;
+ for(auto astModifier : astType->modifiers)
+ {
+ IRAttr* irAttr = (IRAttr*) lowerSimpleVal(context, astModifier);
+ if(irAttr)
+ irAttrs.add(irAttr);
+ }
+
+ auto irType = getBuilder()->getAttributedType(irBase, irAttrs);
+ return LoweredValInfo::simple(irType);
+ }
+
+ LoweredValInfo visitUNormModifierVal(UNormModifierVal* astVal)
+ {
+ SLANG_UNUSED(astVal);
+ return LoweredValInfo::simple(getBuilder()->getAttr(kIROp_UNormAttr));
+ }
+
+ LoweredValInfo visitSNormModifierVal(SNormModifierVal* astVal)
+ {
+ SLANG_UNUSED(astVal);
+ return LoweredValInfo::simple(getBuilder()->getAttr(kIROp_SNormAttr));
+ }
+
// We do not expect to encounter the following types in ASTs that have
// passed front-end semantic checking.
#define UNEXPECTED_CASE(NAME) IRType* visit##NAME(NAME*) { SLANG_UNEXPECTED(#NAME); UNREACHABLE_RETURN(nullptr); }
@@ -3620,6 +3648,12 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
UNREACHABLE_RETURN(LoweredValInfo());
}
+ LoweredValInfo visitModifierCastExpr(
+ ModifierCastExpr* expr)
+ {
+ return this->dispatch(expr->valueArg);
+ }
+
LoweredValInfo subscriptValue(
IRType* type,
LoweredValInfo baseVal,
@@ -3704,6 +3738,12 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
UNREACHABLE_RETURN(LoweredValInfo());
}
+ LoweredValInfo visitModifiedTypeExpr(ModifiedTypeExpr* /*expr*/)
+ {
+ SLANG_UNIMPLEMENTED_X("type expression during code generation");
+ UNREACHABLE_RETURN(LoweredValInfo());
+ }
+
LoweredValInfo visitAssocTypeDecl(AssocTypeDecl* decl)
{
SLANG_UNIMPLEMENTED_X("associatedtype expression during code generation");
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index d9bbaaace..961691c9a 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -213,10 +213,10 @@ namespace Slang
static Decl* parseEnumDecl(Parser* parser);
- static Modifier* ParseOptSemantics(
+ static Modifiers _parseOptSemantics(
Parser* parser);
- static void ParseOptSemantics(
+ static void _parseOptSemantics(
Parser* parser,
Decl* decl);
@@ -234,6 +234,8 @@ namespace Slang
static TokenType peekTokenType(Parser* parser);
+ static Expr* _parseGenericArg(Parser* parser);
+
//
static void Unexpected(
@@ -1228,7 +1230,7 @@ namespace Slang
{
Expr* typeSpec = nullptr;
NameLoc nameAndLoc;
- Modifier* semantics = nullptr;
+ Modifiers semantics;
Expr* initializer = nullptr;
};
@@ -1483,7 +1485,7 @@ namespace Slang
parser->PushScope(decl);
parseParameterList(parser, decl);
- ParseOptSemantics(parser, decl);
+ _parseOptSemantics(parser, decl);
decl->body = parseOptBody(parser);
parser->PopScope();
@@ -1511,9 +1513,9 @@ namespace Slang
}
// Add modifiers to the end of the modifier list for a declaration
- void AddModifiers(Decl* decl, Modifier* modifiers)
+ static void _addModifiers(Decl* decl, Modifiers const& modifiers)
{
- if (!modifiers)
+ if (!modifiers.first)
return;
Modifier** link = &decl->modifiers.first;
@@ -1521,7 +1523,7 @@ namespace Slang
{
link = &(*link)->next;
}
- *link = modifiers;
+ *link = modifiers.first;
}
static Name* generateName(Parser* parser, String const& base)
@@ -1556,7 +1558,7 @@ namespace Slang
}
decl->type = TypeExp(declaratorInfo.typeSpec);
- AddModifiers(decl, declaratorInfo.semantics);
+ _addModifiers(decl, declaratorInfo.semantics);
decl->initExpr = declaratorInfo.initializer;
}
@@ -1695,7 +1697,7 @@ namespace Slang
struct InitDeclarator
{
RefPtr<Declarator> declarator;
- Modifier* semantics = nullptr;
+ Modifiers semantics;
Expr* initializer = nullptr;
};
@@ -1706,7 +1708,7 @@ namespace Slang
{
InitDeclarator result;
result.declarator = parseDeclarator(parser, options);
- result.semantics = ParseOptSemantics(parser);
+ result.semantics = _parseOptSemantics(parser);
return result;
}
@@ -1822,12 +1824,6 @@ namespace Slang
}
};
- // Pares an argument to an application of a generic
- Expr* ParseGenericArg(Parser* parser)
- {
- return parser->ParseArgExpr();
- }
-
// Create a type expression that will refer to the given declaration
static Expr*
createDeclRefType(Parser* parser, Decl* decl)
@@ -1866,10 +1862,10 @@ namespace Slang
parser->ReadToken(TokenType::OpLess);
parser->genericDepth++;
// For now assume all generics have at least one argument
- genericApp->arguments.add(ParseGenericArg(parser));
+ genericApp->arguments.add(_parseGenericArg(parser));
while (AdvanceIf(parser, TokenType::Comma))
{
- genericApp->arguments.add(ParseGenericArg(parser));
+ genericApp->arguments.add(_parseGenericArg(parser));
}
parser->genericDepth--;
@@ -2016,7 +2012,143 @@ namespace Slang
return parseThisTypeExpr(parser);
}
- static TypeSpec parseTypeSpec(Parser* parser)
+ /// Apply the given `modifiers` (if any) to the given `typeExpr`
+ static Expr* _applyModifiersToTypeExpr(Parser* parser, Expr* typeExpr, Modifiers const& modifiers)
+ {
+ if(modifiers.first)
+ {
+ // Currently, we represent a type with modifiers applied to it as
+ // an AST node of the `ModifiedTypeExpr` class. We will create
+ // one here and make it be the home for our `typeModifiers`.
+ //
+ ModifiedTypeExpr* modifiedTypeExpr = parser->astBuilder->create<ModifiedTypeExpr>();
+ modifiedTypeExpr->base.exp = typeExpr;
+ modifiedTypeExpr->modifiers = modifiers;
+ return modifiedTypeExpr;
+ }
+ else
+ {
+ // If none of the modifiers were type modifiers, we can leave
+ // the existing type expression alone.
+ return typeExpr;
+ }
+ }
+
+ /// Apply any type modifier in `ioBaseModifiers` to the given `typeExpr`.
+ ///
+ /// If any type modifiers were present, `ioBaseModifiers` will be updated
+ /// to only include those modifiers that were not type modifiers (if any).
+ ///
+ /// If no type modifiers were present, `ioBaseModifiers` will remain unchanged.
+ ///
+ static Expr* _applyTypeModifiersToTypeExpr(Parser* parser, Expr* typeExpr, Modifiers& ioBaseModifiers)
+ {
+ // The `Modifiers` that were passed in as `ioBaseModifiers` comprise
+ // a singly-linked list of `Modifier` nodes.
+ //
+ // It is possible that some of these modifiers represent type modifiers and,
+ // if so, we want to transfer those modifiers to apply to the type given
+ // by `typeExpr`. Any remaining modifiers that are not type modifiers will
+ // be left in the `ioBaseModifiers` list.
+ //
+ // The type modifiers will be collected into their own `Modifiers` list,
+ // and we will retain a poiner to the final pointer in the linked list
+ // (the one that is null), so that we can append to the end.
+ //
+ Modifiers typeModifiers;
+ Modifier** typeModifierLink = &typeModifiers.first;
+
+ // While iterating over the base modifiers, we need to be able to remove
+ // a linked-list node while inspecting it, so we will similarly keep a "link"
+ // variable that points at whatever location points to the current node
+ // (either the head of the list, or the `next` pointer in the previous modifier)
+ //
+ Modifier** baseModifierLink = &ioBaseModifiers.first;
+ while(auto baseModifier = *baseModifierLink)
+ {
+ // We want to detect whether we have a type modifier or not.
+ //
+ auto typeModifier = as<TypeModifier>(baseModifier);
+
+ // The easy case is when we *don't* have a type modifier.
+ //
+ if(!typeModifier)
+ {
+ // We want to leave the modifier where it is (in the list
+ // of "base" modifiers), and advance to the next one in order.
+ //
+ baseModifierLink = &baseModifier->next;
+ }
+ else
+ {
+ // If we have a type modifier, we need to graft it onto
+ // the list of type modifiers. This is done by writing
+ // a pointer to the type modifier into the "link" for
+ // the type modifier list, and updating the link to point
+ // to the `next` field of the current modifier (since that
+ // fill be the location any further type modifiers need
+ // to be linked).
+ //
+ *typeModifierLink = typeModifier;
+ typeModifierLink = &typeModifier->next;
+
+ // The above logic puts `typeModifier` into the type modifer
+ // list, but it doesn't remove it from the base modifier list.
+ // In order to do that we must replace the pointer to `typeModifer`
+ // with a pointer to whatever is next in the base list, and also
+ // null out the `next` field of `typeModifier` so that it no
+ // longer points to the base modifiers that come after it.
+ //
+ *baseModifierLink = typeModifier->next;
+ typeModifier->next = nullptr;
+
+ // Note: We do *not* need to update `baseModifierLink` before
+ // the next loop iteration, because `*baseModifierLink` has
+ // already been updated so that it points to the next node
+ // we want to visit.
+ }
+ }
+
+ // If we ended up finding any type modifiers, we want to apply them
+ // to the type expression.
+ //
+ return _applyModifiersToTypeExpr(parser, typeExpr, typeModifiers);
+ }
+
+ static TypeSpec _applyModifiersToTypeSpec(Parser* parser, TypeSpec typeSpec, Modifiers const& inModifiers)
+ {
+ // It is possible that the form of the type specifier will have
+ // included a declaration directly (e.g., using `struct { ... }`
+ // as a type specifier to declare both a type and value(s) of that
+ // type in one go).
+ //
+ if(auto decl = typeSpec.decl)
+ {
+ // In the case where there *is* a declaration, we want to apply
+ // any modifiers that logically belong to the type to the type,
+ // and any modifiers that logically belong to the declaration to
+ // the declaration.
+ //
+ Modifiers modifiers = inModifiers;
+ typeSpec.expr = _applyTypeModifiersToTypeExpr(parser, typeSpec.expr, modifiers);
+
+ // Any remaining modifiers should instead be applied to the declaration.
+ _addModifiers(decl, modifiers);
+ }
+ else
+ {
+ // If there are modifiers, then we apply *all* of them to the type expression.
+ // This may result in modifiers being applied that do not belong on a type;
+ // in that case we rely on downstream semantic checking to diagnose any error.
+ //
+ typeSpec.expr = _applyModifiersToTypeExpr(parser, typeSpec.expr, inModifiers);
+ }
+
+ return typeSpec;
+ }
+
+ /// Parse a type specifier, without dealing with modifiers.
+ static TypeSpec _parseSimpleTypeSpec(Parser* parser)
{
TypeSpec typeSpec;
@@ -2110,13 +2242,56 @@ namespace Slang
return typeSpec;
}
+ /// Parse a type specifier, following the given list of modifiers.
+ ///
+ /// If there are any modifiers in `ioModifiers`, this function may modify it
+ /// by stripping out any type modifiers and attaching them to the `TypeSpec`.
+ /// Any modifiers that are not type modifiers will be left where they were.
+ ///
+ static TypeSpec _parseTypeSpec(Parser* parser, Modifiers& ioModifiers)
+ {
+ TypeSpec typeSpec = _parseSimpleTypeSpec(parser);
+
+ // We don't know whether `ioModifiers` has any modifiers in it,
+ // or which of them might be type modifiers, so we will delegate
+ // figuring that out to a subroutine.
+ //
+ typeSpec.expr = _applyTypeModifiersToTypeExpr(parser, typeSpec.expr, ioModifiers);
+
+ return typeSpec;
+ }
+
+ /// Parse a type specifier, including any leading modifiers.
+ ///
+ /// Note that all the modifiers that precede the type specifier
+ /// will end up as modifiers for the type specifier even if they
+ /// should *not* be allowed as modifiers on a type.
+ ///
+ /// This function should not be used in contexts where a type specifier
+ /// is being parsed as part of a declaration, such that a subset of
+ /// the modifiers might inhere to the declaration rather than the
+ /// type specifier.
+ ///
+ static TypeSpec _parseTypeSpec(Parser* parser)
+ {
+ Modifiers modifiers = ParseModifiers(parser);
+ TypeSpec typeSpec = _parseSimpleTypeSpec(parser);
+
+ typeSpec = _applyModifiersToTypeSpec(parser, typeSpec, modifiers);
+
+ return typeSpec;
+ }
+
+
static DeclBase* ParseDeclaratorDecl(
- Parser* parser,
- ContainerDecl* containerDecl)
+ Parser* parser,
+ ContainerDecl* containerDecl,
+ Modifiers const& inModifiers)
{
SourceLoc startPosition = parser->tokenReader.peekLoc();
- auto typeSpec = parseTypeSpec(parser);
+ Modifiers modifiers = inModifiers;
+ auto typeSpec = _parseTypeSpec(parser, modifiers);
// We may need to build up multiple declarations in a group,
// but the common case will be when we have just a single
@@ -2206,7 +2381,7 @@ namespace Slang
// Only parse as a function if we didn't already see mutually-exclusive
// constructs when parsing the declarator.
&& !initDeclarator.initializer
- && !initDeclarator.semantics)
+ && !initDeclarator.semantics.first)
{
// Looks like a function, so parse it like one.
UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo);
@@ -2433,14 +2608,15 @@ namespace Slang
//
// opt-semantics ::= (':' semantic)*
//
- static Modifier* ParseOptSemantics(
+ static Modifiers _parseOptSemantics(
Parser* parser)
{
+ Modifiers modifiers;
+
if (!AdvanceIf(parser, TokenType::Colon))
- return nullptr;
+ return modifiers;
- Modifier* result = nullptr;
- Modifier** link = &result;
+ Modifier** link = &modifiers.first;
SLANG_ASSERT(!*link);
for (;;)
@@ -2470,18 +2646,18 @@ namespace Slang
// avoiding an infinite loop here.
if (!AdvanceIf(parser, TokenType::Colon))
{
- return result;
+ return modifiers;
}
}
}
- static void ParseOptSemantics(
+ static void _parseOptSemantics(
Parser* parser,
Decl* decl)
{
- AddModifiers(decl, ParseOptSemantics(parser));
+ _addModifiers(decl, _parseOptSemantics(parser));
}
static Decl* ParseHLSLBufferDecl(
@@ -2559,7 +2735,7 @@ namespace Slang
// Any semantics applied to the buffer declaration are taken as applying
// to the variable instead.
- ParseOptSemantics(parser, bufferVarDecl);
+ _parseOptSemantics(parser, bufferVarDecl);
// The declarations in the body belong to the data type.
parseDeclBody(parser, bufferDataTypeDecl);
@@ -2917,7 +3093,7 @@ namespace Slang
}
decl->loc = loc;
- AddModifiers(decl, modifiers.first);
+ _addModifiers(decl, modifiers);
parser->PushScope(decl);
@@ -3423,7 +3599,7 @@ namespace Slang
Decl* declToModify = decl;
if(auto genericDecl = as<GenericDecl>(decl))
declToModify = genericDecl->inner;
- AddModifiers(declToModify, modifiers.first);
+ _addModifiers(declToModify, modifiers);
// Make sure the decl is properly nested inside its lexical parent
if (containerDecl)
@@ -3435,7 +3611,7 @@ namespace Slang
static DeclBase* ParseDeclWithModifiers(
Parser* parser,
ContainerDecl* containerDecl,
- Modifiers modifiers )
+ Modifiers modifiers )
{
DeclBase* decl = nullptr;
@@ -3462,7 +3638,7 @@ namespace Slang
// Our final fallback case is to assume that the user is
// probably writing a C-style declarator-based declaration.
- decl = ParseDeclaratorDecl(parser, containerDecl);
+ decl = ParseDeclaratorDecl(parser, containerDecl, modifiers);
break;
}
break;
@@ -3483,7 +3659,7 @@ namespace Slang
// If nothing else matched, we try to parse an "ordinary" declarator-based declaration
default:
- decl = ParseDeclaratorDecl(parser, containerDecl);
+ decl = ParseDeclaratorDecl(parser, containerDecl, modifiers);
break;
}
@@ -4299,7 +4475,7 @@ namespace Slang
///
static Expr* _parseAtomicTypeExpr(Parser* parser)
{
- auto typeSpec = parseTypeSpec(parser);
+ auto typeSpec = _parseTypeSpec(parser);
if( typeSpec.decl )
{
AddMember(parser->currentScope, typeSpec.decl);
@@ -4318,15 +4494,8 @@ namespace Slang
return parsePostfixTypeSuffix(parser, typeExpr);
}
- /// Parse an infix type expression.
- ///
- /// Currently, the only infix type expression we support is the `&`
- /// operator for forming interface conjunctions.
- ///
- static Expr* _parseInfixTypeExpr(Parser* parser)
+ static Expr* _parseInfixTypeExprSuffix(Parser* parser, Expr* leftExpr)
{
- auto leftExpr = _parsePostfixTypeExpr(parser);
-
for(;;)
{
// As long as the next token is an `&`, we will try
@@ -4349,6 +4518,17 @@ namespace Slang
return leftExpr;
}
+ /// Parse an infix type expression.
+ ///
+ /// Currently, the only infix type expression we support is the `&`
+ /// operator for forming interface conjunctions.
+ ///
+ static Expr* _parseInfixTypeExpr(Parser* parser)
+ {
+ auto leftExpr = _parsePostfixTypeExpr(parser);
+ return _parseInfixTypeExprSuffix(parser, leftExpr);
+ }
+
Expr* Parser::ParseType()
{
return _parseInfixTypeExpr(this);
@@ -5508,6 +5688,46 @@ namespace Slang
return parsePrefixExpr(this);
}
+ /// Parse an argument to an application of a generic
+ static Expr* _parseGenericArg(Parser* parser)
+ {
+ // The grammar for generic arguments needs to be a super-set of the
+ // grammar for types and for expressions, because we do not know
+ // which to expect at each argument position during parsing.
+ //
+ // For the most part the expression grammar is more permissive than
+ // the type grammar, but types support modifiers that are not
+ // (currently) allowed in pure expression contexts.
+ //
+ // We could in theory allow modifiers to appear in expression contexts
+ // and deal with the cases where this should not be allowed downstream,
+ // but doing so runs a high risk of changing the meaning of existing code
+ // (notably in cases where a user might have used a variable name that
+ // overlaps with a language modifier keyword).
+ //
+ // Instead, we will simply detect the case where modifiers appear on
+ // a generic argument here, as a special case.
+ //
+ Modifiers modifiers = ParseModifiers(parser);
+ if(modifiers.first)
+ {
+ // If there are any modifiers, then we know that we are actually
+ // in the type case.
+ //
+ auto typeSpec = _parseSimpleTypeSpec(parser);
+ typeSpec = _applyModifiersToTypeSpec(parser, typeSpec, modifiers);
+
+ auto typeExpr = typeSpec.expr;
+
+ typeExpr = parsePostfixTypeSuffix(parser, typeExpr);
+ typeExpr = _parseInfixTypeExprSuffix(parser, typeExpr);
+
+ return typeExpr;
+ }
+
+ return parser->ParseArgExpr();
+ }
+
Expr* parseTermFromSourceFile(
ASTBuilder* astBuilder,
TokenSpan const& tokens,