diff options
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/core.meta.slang | 36 | ||||
| -rw-r--r-- | source/slang/slang-ast-builder.cpp | 72 | ||||
| -rw-r--r-- | source/slang/slang-ast-builder.h | 65 | ||||
| -rw-r--r-- | source/slang/slang-ast-expr.h | 26 | ||||
| -rw-r--r-- | source/slang/slang-ast-modifier.h | 40 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.cpp | 97 | ||||
| -rw-r--r-- | source/slang/slang-ast-type.h | 15 | ||||
| -rw-r--r-- | source/slang/slang-ast-val.cpp | 46 | ||||
| -rw-r--r-- | source/slang/slang-ast-val.h | 35 | ||||
| -rw-r--r-- | source/slang/slang-check-conversion.cpp | 145 | ||||
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 49 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 9 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 56 | ||||
| -rw-r--r-- | source/slang/slang-emit-c-like.h | 16 | ||||
| -rw-r--r-- | source/slang/slang-emit-hlsl.cpp | 37 | ||||
| -rw-r--r-- | source/slang/slang-emit-hlsl.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 24 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 25 | ||||
| -rw-r--r-- | source/slang/slang-ir.h | 10 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 40 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 310 |
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, |
