From 7f7864e80e8b5631ba5ec1aa9657fdaf1b4adb06 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Wed, 21 Jun 2017 10:43:25 -0700 Subject: Support texture `Gather*()` operations The catch with these operations is that they return a vector based on the scalar of the element type of the texture. That is, given `Texture2D t` the operation `t.GatherRed(...)` should return a `float4`. The ideal way to solve this would use associated types, but we aren't there yet, so I am using extension declarations. An extension can "capture" the identity of the element type, like so: __generic __extension Texture2D > { ... } That extension will match `Texture2D` and correctly capture `T == float`, so that we can use it in other operations. Getting this working required a bunch of changes: - Actually emit the relevant extension declarations in the stdlib - Fix the parser to be able to parse `Texture2D >` (that is, a nested generic app). - I actually went ahead and significantly overhauled the expression parser while I was there, because I just couldn't deal with the existing code any longer. - Added support for general-case lookup to look through `__extension` declarations. I had logic in place to special-case this for looking up "constructors" but hadn't done anything for general member lookup yet. - This required some annoying holes to be punched through the layers, because lookup might need to invoke semantic analysis to ensure that an extension has been checked. - There is some first-pass code trying to support looking up a `typedef` nested inside the `vector` type. This is a nice idea in principle, but the problem is that the `Texture2D` definition would be looking up `T.Element` and not `float4.Element`, and that means we'd need machinery for doing lookup *through* interface conformances for a type parameter like `T` The big gotcha here is that none of this logic applies to `Texture2D` (the original case I mentioned) because I am matching vector types and not scalars. Matching scalars *should* be as easy as: __generic __extension Texture2D { ... } But I'd need to confirm that interface constraints like that actually work, or else that extension would *also* apply to `Texture2D` and break everything. --- source/slang/check.cpp | 132 +++++--------- source/slang/lookup.cpp | 70 ++++++- source/slang/lookup.h | 12 +- source/slang/parser.cpp | 415 +++++++++++++++++++++++++++++++++++++++--- source/slang/slang-stdlib.cpp | 101 +++++++++- source/slang/syntax.h | 12 +- 6 files changed, 616 insertions(+), 126 deletions(-) (limited to 'source') diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 5a82380d3..463052bc8 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -2269,7 +2269,7 @@ namespace Slang // Note(tfoley): The name used for lookup here is a bit magical, since // it must match what the parser installed in subscript declarations. - LookupResult lookupResult = LookUpLocal("operator[]", aggTypeDeclRef); + LookupResult lookupResult = LookUpLocal(this, "operator[]", aggTypeDeclRef); if (!lookupResult.isValid()) { goto fail; @@ -4552,7 +4552,7 @@ namespace Slang expr->Type = ExpressionType::Error; - auto lookupResult = LookUp(expr->name, expr->scope); + auto lookupResult = LookUp(this, expr->name, expr->scope); if (lookupResult.isValid()) { return createLookupResultExpr( @@ -4795,6 +4795,38 @@ namespace Slang baseScalarType, 1); } + else if(auto typeType = baseType->As()) + { + // We are looking up a member inside a type. + // We want to be careful here because we should only find members + // that are implicitly or explicitly `static`. + // + // TODO: this duplicates a *lot* of logic with the case below. + // We need to fix that. + auto type = typeType->type; + if(auto declRefType = type->AsDeclRefType()) + { + if (auto aggTypeDeclRef = declRefType->declRef.As()) + { + // Checking of the type must be complete before we can reference its members safely + EnsureDecl(aggTypeDeclRef.getDecl(), DeclCheckState::Checked); + + LookupResult lookupResult = LookUpLocal(this, expr->name, aggTypeDeclRef); + if (!lookupResult.isValid()) + { + goto fail; + } + + // TODO: need to filter for declarations that are valid to refer + // to in this context... + + return createLookupResultExpr( + lookupResult, + expr->BaseExpression, + expr); + } + } + } else if (auto declRefType = baseType->AsDeclRefType()) { if (auto aggTypeDeclRef = declRefType->declRef.As()) @@ -4802,8 +4834,7 @@ namespace Slang // Checking of the type must be complete before we can reference its members safely EnsureDecl(aggTypeDeclRef.getDecl(), DeclCheckState::Checked); - - LookupResult lookupResult = LookUpLocal(expr->name, aggTypeDeclRef); + LookupResult lookupResult = LookUpLocal(this, expr->name, aggTypeDeclRef); if (!lookupResult.isValid()) { goto fail; @@ -4813,88 +4844,6 @@ namespace Slang lookupResult, expr->BaseExpression, expr); -#if 0 - DeclRef memberDeclRef(lookupResult.decl, aggTypeDeclRef.substitutions); - return ConstructDeclRefExpr(memberDeclRef, expr->BaseExpression, expr); -#endif - -#if 0 - - - // TODO(tfoley): It is unfortunate that the lookup strategy - // here isn't unified with the ordinary `Scope` case. - // In particular, if we add support for "transparent" declarations, - // etc. here then we would need to add them in ordinary lookup - // as well. - - Decl* memberDecl = nullptr; // The first declaration we found, if any - Decl* secondDecl = nullptr; // Another declaration with the same name, if any - for (auto m : aggTypeDeclRef.GetMembers()) - { - if (m.GetName() != expr->MemberName) - continue; - - if (!memberDecl) - { - memberDecl = m.getDecl(); - } - else - { - secondDecl = m.getDecl(); - break; - } - } - - // If we didn't find any member, then we signal an error - if (!memberDecl) - { - expr->Type = ExpressionType::Error; - getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->MemberName, baseType); - return expr; - } - - // If we found only a single member, then we are fine - if (!secondDecl) - { - // TODO: need to - DeclRef memberDeclRef(memberDecl, aggTypeDeclRef.substitutions); - - expr->declRef = memberDeclRef; - expr->Type = GetTypeForDeclRef(memberDeclRef); - - // When referencing a member variable, the result is an l-value - // if and only if the base expression was. - if (auto memberVarDecl = dynamic_cast(memberDecl)) - { - expr->Type.IsLeftValue = expr->BaseExpression->Type.IsLeftValue; - } - return expr; - } - - // We found multiple members with the same name, and need - // to resolve the embiguity at some point... - expr->Type = ExpressionType::Error; - getSink()->diagnose(expr, Diagnostics::unimplemented, "ambiguous member reference"); - return expr; - -#endif - -#if 0 - - StructField* field = structDecl->FindField(expr->MemberName); - if (!field) - { - expr->Type = ExpressionType::Error; - getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->MemberName, baseType); - } - else - expr->Type = field->Type; - - // A reference to a struct member is an l-value if the reference to the struct - // value was also an l-value. - expr->Type.IsLeftValue = expr->BaseExpression->Type.IsLeftValue; - return expr; -#endif } // catch-all @@ -5048,4 +4997,15 @@ namespace Slang return getTypeForDeclRef(nullptr, nullptr, declRef, &typeResult); } + DeclRef ApplyExtensionToType( + SemanticsVisitor* semantics, + ExtensionDecl* extDecl, + RefPtr type) + { + if(!semantics) + return DeclRef(); + + return semantics->ApplyExtensionToType(extDecl, type); + } + } diff --git a/source/slang/lookup.cpp b/source/slang/lookup.cpp index e9c2177de..f5e472ec5 100644 --- a/source/slang/lookup.cpp +++ b/source/slang/lookup.cpp @@ -5,6 +5,14 @@ namespace Slang { // +DeclRef ApplyExtensionToType( + SemanticsVisitor* semantics, + ExtensionDecl* extDecl, + RefPtr type); + +// + + // Helper for constructing breadcrumb trails during lookup, without unnecessary heap allocaiton struct BreadcrumbInfo { @@ -190,7 +198,7 @@ void DoMemberLookupImpl( // Look for members of the given name in the given container for declarations void DoLocalLookupImpl( String const& name, - DeclRef containerDeclRef, + DeclRef containerDeclRef, LookupRequest const& request, LookupResult& result, BreadcrumbInfo* inBreadcrumbs) @@ -241,7 +249,24 @@ void DoLocalLookupImpl( DoMemberLookupImpl(name, transparentMemberDeclRef, request, result, &memberRefBreadcrumb); } - // TODO(tfoley): need to consider lookup via extension here? + // Consider lookup via extension + if( auto aggTypeDeclRef = containerDeclRef.As() ) + { + RefPtr type = DeclRefType::Create(aggTypeDeclRef); + + for (auto ext = GetCandidateExtensions(aggTypeDeclRef); ext; ext = ext->nextCandidateExtension) + { + auto extDeclRef = ApplyExtensionToType(request.semantics, ext, type); + if (!extDeclRef) + continue; + + // TODO: eventually we need to insert a breadcrumb here so that + // the constructed result can somehow indicate that a member + // was found through an extension. + + DoLocalLookupImpl(name, extDeclRef, request, result, inBreadcrumbs); + } + } } void DoLookupImpl( @@ -258,10 +283,34 @@ void DoLookupImpl( // also finding a hit in another for(auto link = scope; link; link = link->nextSibling) { - if(!link->containerDecl) + auto containerDecl = link->containerDecl; + + if(!containerDecl) continue; - DeclRef containerRef = DeclRef(link->containerDecl, nullptr).As(); + // If the container is a generic, then we need to instantiate it + // at the parameters themselves, so provide a fully-resolved + // declaration reference for lookup. + RefPtr subst = nullptr; + if(auto parentGenericDecl = dynamic_cast(containerDecl->ParentDecl)) + { + subst = new Substitutions(); + subst->genericDecl = parentGenericDecl; + + for( auto pp : parentGenericDecl->Members ) + { + if( auto genericTypeParam = pp.As() ) + { + subst->args.Add(DeclRefType::Create(DeclRef(genericTypeParam.Ptr(), nullptr))); + } + else if( auto genericValParam = pp.As() ) + { + subst->args.Add(new GenericParamIntVal(DeclRef(genericValParam.Ptr(), nullptr))); + } + } + } + + DeclRef containerRef = DeclRef(containerDecl, subst).As(); DoLocalLookupImpl(name, containerRef, request, result, nullptr); } @@ -283,18 +332,27 @@ LookupResult DoLookup(String const& name, LookupRequest const& request) return result; } -LookupResult LookUp(String const& name, RefPtr scope) +LookupResult LookUp( + SemanticsVisitor* semantics, + String const& name, + RefPtr scope) { LookupRequest request; + request.semantics = semantics; request.scope = scope; return DoLookup(name, request); } // perform lookup within the context of a particular container declaration, // and do *not* look further up the chain -LookupResult LookUpLocal(String const& name, DeclRef containerDeclRef) +LookupResult LookUpLocal( + SemanticsVisitor* semantics, + String const& name, + DeclRef containerDeclRef) { LookupRequest request; + request.semantics = semantics; + LookupResult result; DoLocalLookupImpl(name, containerDeclRef, request, result, nullptr); return result; diff --git a/source/slang/lookup.h b/source/slang/lookup.h index eb1888878..52eaa9804 100644 --- a/source/slang/lookup.h +++ b/source/slang/lookup.h @@ -5,6 +5,8 @@ namespace Slang { +class SemanticsVisitor; + // Take an existing lookup result and refine it to only include // results that pass the given `LookupMask`. LookupResult refineLookup(LookupResult const& inResult, LookupMask mask); @@ -15,11 +17,17 @@ void buildMemberDictionary(ContainerDecl* decl); // Look up a name in the given scope, proceeding up through // parent scopes as needed. -LookupResult LookUp(String const& name, RefPtr scope); +LookupResult LookUp( + SemanticsVisitor* semantics, + String const& name, + RefPtr scope); // perform lookup within the context of a particular container declaration, // and do *not* look further up the chain -LookupResult LookUpLocal(String const& name, DeclRef containerDeclRef); +LookupResult LookUpLocal( + SemanticsVisitor* semantics, + String const& name, + DeclRef containerDeclRef); // TODO: this belongs somewhere else diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index 48954dc04..0598e67a7 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -750,6 +750,7 @@ namespace Slang if( parser->LookAheadToken(TokenType::Identifier) ) { LookupResult lookupResult = LookUp( + nullptr, // No semantics visitor available yet! parser->tokenReader.PeekToken().Content, parser->currentScope); @@ -1331,6 +1332,32 @@ namespace Slang RefPtr expr; }; + static RefPtr parseGenericApp( + Parser* parser, + RefPtr base) + { + RefPtr genericApp = new GenericAppExpr(); + + parser->FillPosition(genericApp.Ptr()); // set up scope for lookup + genericApp->FunctionExpr = base; + parser->ReadToken(TokenType::OpLess); + parser->genericDepth++; + // For now assume all generics have at least one argument + genericApp->Arguments.Add(ParseGenericArg(parser)); + while (AdvanceIf(parser, TokenType::Comma)) + { + genericApp->Arguments.Add(ParseGenericArg(parser)); + } + parser->genericDepth--; + + // TODO: probably want to suppot `>>` and `>=` here as well, + // and split up those tokens as needed. + // + parser->ReadToken(TokenType::OpGreater); + + return genericApp; + } + static TypeSpec parseTypeSpec(Parser* parser) { @@ -1367,21 +1394,7 @@ namespace Slang if (parser->LookAheadToken(TokenType::OpLess)) { - RefPtr gtype = new GenericAppExpr(); - parser->FillPosition(gtype.Ptr()); // set up scope for lookup - gtype->Position = typeName.Position; - gtype->FunctionExpr = typeExpr; - parser->ReadToken(TokenType::OpLess); - parser->genericDepth++; - // For now assume all generics have at least one argument - gtype->Arguments.Add(ParseGenericArg(parser)); - while (AdvanceIf(parser, TokenType::Comma)) - { - gtype->Arguments.Add(ParseGenericArg(parser)); - } - parser->genericDepth--; - parser->ReadToken(TokenType::OpGreater); - typeExpr = gtype; + typeExpr = parseGenericApp(parser, typeExpr); } typeSpec.expr = typeExpr; @@ -1959,10 +1972,10 @@ namespace Slang { AddMember(decl, ParseGenericParamDecl(parser, decl)); -if( parser->LookAheadToken(TokenType::OpGreater) ) -break; + if( parser->LookAheadToken(TokenType::OpGreater) ) + break; -parser->ReadToken(TokenType::Comma); + parser->ReadToken(TokenType::Comma); } parser->genericDepth--; parser->ReadToken(TokenType::OpGreater); @@ -2370,14 +2383,33 @@ parser->ReadToken(TokenType::Comma); return stmt; } - static bool peekTypeName(Parser* parser) + static bool isGenericName(Parser* parser, String const& name) { - if(!parser->LookAheadToken(TokenType::Identifier)) + auto lookupResult = LookUp( + nullptr, // no semantics visitor available yet + name, + parser->currentScope); + if(!lookupResult.isValid() || lookupResult.isOverloaded()) return false; - auto name = parser->tokenReader.PeekToken().Content; + auto decl = lookupResult.item.declRef.getDecl(); + if( auto genericDecl = dynamic_cast(decl) ) + { + return true; + } + else + { + return false; + } + } + - auto lookupResult = LookUp(name, parser->currentScope); + static bool isTypeName(Parser* parser, String const& name) + { + auto lookupResult = LookUp( + nullptr, // no semantics visitor available yet + name, + parser->currentScope); if(!lookupResult.isValid() || lookupResult.isOverloaded()) return false; @@ -2396,6 +2428,15 @@ parser->ReadToken(TokenType::Comma); } } + static bool peekTypeName(Parser* parser) + { + if(!parser->LookAheadToken(TokenType::Identifier)) + return false; + + auto name = parser->tokenReader.PeekToken().Content; + return isTypeName(parser, name); + } + RefPtr Parser::ParseStatement() { auto modifiers = ParseModifiers(this); @@ -2766,6 +2807,8 @@ parser->ReadToken(TokenType::Comma); { switch(type) { + case TokenType::QuestionMark: + return Precedence::TernaryConditional; case TokenType::Comma: return Precedence::Comma; case TokenType::OpAssign: @@ -2918,8 +2961,79 @@ parser->ReadToken(TokenType::Comma); } + static RefPtr createInfixExpr( + Parser* /*parser*/, + RefPtr left, + RefPtr op, + RefPtr right) + { + RefPtr expr = new InfixExpr(); + expr->Position = op->Position; + expr->FunctionExpr = op; + expr->Arguments.Add(left); + expr->Arguments.Add(right); + return expr; + } + + static RefPtr parseInfixExprWithPrecedence( + Parser* parser, + RefPtr inExpr, + Precedence prec) + { + auto expr = inExpr; + for(;;) + { + auto opTokenType = parser->tokenReader.PeekTokenType(); + auto opPrec = GetOpLevel(parser, opTokenType); + if(opPrec < prec) + break; + + auto op = parseOperator(parser); + + // Special case the `?:` operator since it is the + // one non-binary case we need to deal with. + if(opTokenType == TokenType::QuestionMark) + { + RefPtr select = new SelectExpressionSyntaxNode(); + select->Position = op->Position; + select->FunctionExpr = op; + + select->Arguments.Add(expr); + + select->Arguments.Add(parser->ParseExpression(opPrec)); + parser->ReadToken(TokenType::Colon); + select->Arguments.Add(parser->ParseExpression(opPrec)); + + expr = select; + continue; + } + + auto right = parser->ParseLeafExpression(); + + for(;;) + { + auto nextOpPrec = GetOpLevel(parser, parser->tokenReader.PeekTokenType()); + + if((GetAssociativityFromLevel(nextOpPrec) == Associativity::Right) && (nextOpPrec != opPrec)) + break; + else if( nextOpPrec <= opPrec) + break; + + right = parseInfixExprWithPrecedence(parser, right, nextOpPrec); + } + + expr = createInfixExpr(parser, expr, op, right); + } + return expr; + } + RefPtr Parser::ParseExpression(Precedence level) { + auto expr = ParseLeafExpression(); + return parseInfixExprWithPrecedence(this, expr, level); + +#if 0 + if (level == Precedence::Prefix) return ParseLeafExpression(); if (level == Precedence::TernaryConditional) @@ -2975,10 +3089,266 @@ parser->ReadToken(TokenType::Comma); return left; } } +#endif + } + + static TokenType peekTokenType(Parser* parser) + { + return parser->tokenReader.PeekTokenType(); + } + + // We *might* be looking at an application of a generic to arguments, + // but we need to disambiguate to make sure. + static RefPtr maybeParseGenericApp( + Parser* parser, + + // TODO: need to support more general expressions here + RefPtr base) + { + if(peekTokenType(parser) != TokenType::OpLess) + return base; + + if(!isGenericName(parser, base->name)) + return base; + + // Okay, seems likely that we are looking at a generic app + + return parseGenericApp(parser, base); + } + + static RefPtr parseAtomicExpr(Parser* parser) + { + switch( peekTokenType(parser) ) + { + default: + // TODO: should this return an error expression instead of NULL? + parser->sink->diagnose(parser->tokenReader.PeekLoc(), Diagnostics::syntaxError); + return nullptr; + + // Either: + // - parenthized expression `(exp)` + // - cast `(type) exp` + // + // Proper disambiguation requires mixing up parsing + // and semantic checking (which we should do eventually) + // but for now we will follow some hueristics. + case TokenType::LParent: + { + parser->ReadToken(TokenType::LParent); + + RefPtr expr; + if (peekTypeName(parser) && parser->LookAheadToken(TokenType::RParent, 1)) + { + RefPtr tcexpr = new TypeCastExpressionSyntaxNode(); + parser->FillPosition(tcexpr.Ptr()); + tcexpr->TargetType = parser->ParseTypeExp(); + parser->ReadToken(TokenType::RParent); + tcexpr->Expression = parser->ParseExpression(Precedence::Multiplicative); // Note(tfoley): need to double-check this + expr = tcexpr; + } + else + { + expr = parser->ParseExpression(); + parser->ReadToken(TokenType::RParent); + } + + return expr; + } + + // An initializer list `{ expr, ... }` + case TokenType::LBrace: + { + RefPtr initExpr = new InitializerListExpr(); + parser->FillPosition(initExpr.Ptr()); + + // Initializer list + parser->ReadToken(TokenType::LBrace); + + List> exprs; + + for(;;) + { + if(AdvanceIfMatch(parser, TokenType::RBrace)) + break; + + auto expr = parser->ParseArgExpr(); + if( expr ) + { + initExpr->args.Add(expr); + } + + if(AdvanceIfMatch(parser, TokenType::RBrace)) + break; + + parser->ReadToken(TokenType::Comma); + } + + return initExpr; + } + + case TokenType::IntLiterial: + case TokenType::DoubleLiterial: + { + RefPtr constExpr = new ConstantExpressionSyntaxNode(); + auto token = parser->tokenReader.AdvanceToken(); + parser->FillPosition(constExpr.Ptr()); + if (token.Type == TokenType::IntLiterial) + { + constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Int; + constExpr->IntValue = StringToInt(token.Content); + } + else if (token.Type == TokenType::DoubleLiterial) + { + constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Float; + constExpr->FloatValue = (FloatingPointLiteralValue) StringToDouble(token.Content); + } + + return constExpr; + } + + case TokenType::Identifier: + { + // TODO(tfoley): Need a name-lookup step here to resolve + // syntactic keywords in expression context. + + if (parser->LookAheadToken("true") || parser->LookAheadToken("false")) + { + RefPtr constExpr = new ConstantExpressionSyntaxNode(); + auto token = parser->tokenReader.AdvanceToken(); + parser->FillPosition(constExpr.Ptr()); + constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Bool; + constExpr->IntValue = token.Content == "true" ? 1 : 0; + + return constExpr; + } + + // Default behavior is just to create a name expression + RefPtr varExpr = new VarExpressionSyntaxNode(); + varExpr->scope = parser->currentScope.Ptr(); + parser->FillPosition(varExpr.Ptr()); + auto token = parser->ReadToken(TokenType::Identifier); + varExpr->name = token.Content; + + if(peekTokenType(parser) == TokenType::OpLess) + { + return maybeParseGenericApp(parser, varExpr); + } + + return varExpr; + } + } + } + + static RefPtr parsePostfixExpr(Parser* parser) + { + auto expr = parseAtomicExpr(parser); + for(;;) + { + switch( peekTokenType(parser) ) + { + default: + return expr; + + // Postfix increment/decrement + case TokenType::OpInc: + case TokenType::OpDec: + { + RefPtr postfixExpr = new PostfixExpr(); + parser->FillPosition(postfixExpr.Ptr()); + postfixExpr->FunctionExpr = parseOperator(parser); + postfixExpr->Arguments.Add(expr); + + expr = postfixExpr; + } + break; + + // Subscript operation `a[i]` + case TokenType::LBracket: + { + RefPtr indexExpr = new IndexExpressionSyntaxNode(); + indexExpr->BaseExpression = expr; + parser->FillPosition(indexExpr.Ptr()); + parser->ReadToken(TokenType::LBracket); + indexExpr->IndexExpression = parser->ParseExpression(); + parser->ReadToken(TokenType::RBracket); + + expr = indexExpr; + } + break; + + // Call oepration `f(x)` + case TokenType::LParent: + { + RefPtr invokeExpr = new InvokeExpressionSyntaxNode(); + invokeExpr->FunctionExpr = expr; + parser->FillPosition(invokeExpr.Ptr()); + parser->ReadToken(TokenType::LParent); + while (!parser->tokenReader.IsAtEnd()) + { + if (!parser->LookAheadToken(TokenType::RParent)) + invokeExpr->Arguments.Add(parser->ParseArgExpr()); + else + { + break; + } + if (!parser->LookAheadToken(TokenType::Comma)) + break; + parser->ReadToken(TokenType::Comma); + } + parser->ReadToken(TokenType::RParent); + + expr = invokeExpr; + } + break; + + // Member access `x.m` + case TokenType::Dot: + { + RefPtr memberExpr = new MemberExpressionSyntaxNode(); + + // TODO(tfoley): why would a member expression need this? + memberExpr->scope = parser->currentScope.Ptr(); + + parser->FillPosition(memberExpr.Ptr()); + memberExpr->BaseExpression = expr; + parser->ReadToken(TokenType::Dot); + memberExpr->name = parser->ReadToken(TokenType::Identifier).Content; + + expr = memberExpr; + } + break; + } + } + } + + static RefPtr parsePrefixExpr(Parser* parser) + { + switch( peekTokenType(parser) ) + { + default: + return parsePostfixExpr(parser); + + case TokenType::OpInc: + case TokenType::OpDec: + case TokenType::OpNot: + case TokenType::OpBitNot: + case TokenType::OpSub: + { + RefPtr prefixExpr = new PrefixExpr(); + parser->FillPosition(prefixExpr.Ptr()); + prefixExpr->FunctionExpr = parseOperator(parser); + prefixExpr->Arguments.Add(parsePrefixExpr(parser)); + return prefixExpr; + } + break; + } } RefPtr Parser::ParseLeafExpression() { + return parsePrefixExpr(this); + +#if 0 RefPtr rs; if (LookAheadToken(TokenType::OpInc) || LookAheadToken(TokenType::OpDec) || @@ -3150,6 +3520,7 @@ parser->ReadToken(TokenType::Comma); sink->diagnose(tokenReader.PeekLoc(), Diagnostics::syntaxError); } return rs; +#endif } // Parse a source file into an existing translation unit diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp index 387b6fbb2..3a3f6d13f 100644 --- a/source/slang/slang-stdlib.cpp +++ b/source/slang/slang-stdlib.cpp @@ -1150,6 +1150,7 @@ namespace Slang // Declare vector and matrix types sb << "__generic __magic_type(Vector) struct vector\n{\n"; + sb << " typedef T Element;\n"; sb << " __init(T value);\n"; // initialize from single scalar sb << "};\n"; sb << "__generic __magic_type(Matrix) struct matrix {};\n"; @@ -1343,13 +1344,9 @@ namespace Slang sb << "float CalculateLevelOfDetailUnclamped(SamplerState s, "; sb << "float" << kBaseTextureTypes[tt].coordCount << " location);\n"; - - // TODO: `Gather` operation - // (tricky because it returns a 4-vector of the element type - // of the texture components...) } - // TODO: `GetDimensions` operations + // `GetDimensions` for(int isFloat = 0; isFloat < 2; ++isFloat) for(int includeMipInfo = 0; includeMipInfo < 2; ++includeMipInfo) @@ -1532,12 +1529,104 @@ namespace Slang } sb << "\n};\n"; + + // `Gather*()` operations are handled via an `extension` declaration, + // because this lets us capture the element type of the texture. + // + // TODO: longer-term there should be something like a `TextureElementType` + // interface, that both scalars and vectors implement, that then exposes + // a `Scalar` associated type, and `Gather` can return `vector`. + // + static const struct { + char const* genericPrefix; + char const* elementType; + } kGatherExtensionCases[] = { + { "__generic", "vector" }, + + // TODO: need a case here for scalars `T`, but also + // need to ensure that case doesn't accidentally match + // for `T = vector<...>`, which requires actual checking + // of constraints on generic parameters. + }; + for(auto cc : kGatherExtensionCases) + { + // TODO: this should really be an `if` around the entire `Gather` logic + if (isMultisample) break; + + EMIT_LINE_DIRECTIVE(); + sb << cc.genericPrefix << " __extension "; + sb << kBaseTextureAccessLevels[accessLevel].name; + sb << name; + if (isArray) sb << "Array"; + sb << "<" << cc.elementType << " >"; + sb << "\n{\n"; + + + // `Gather` + // (tricky because it returns a 4-vector of the element type + // of the texture components...) + // + // TODO: is it actually correct to restrict these so that, e.g., + // `GatherAlpha()` isn't allowed on `Texture2D` because + // it nominally doesn't have an alpha component? + static const struct { + int componentIndex; + char const* componentName; + } kGatherComponets[] = { + { 0, "" }, + { 0, "Red" }, + { 1, "Green" }, + { 2, "Blue" }, + { 3, "Alpha" }, + }; + + for(auto cc : kGatherComponets) + { + auto componentIndex = cc.componentIndex; + auto componentName = cc.componentName; + + EMIT_LINE_DIRECTIVE(); + sb << "vector Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location);\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "vector Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset);\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "vector Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset, "; + sb << "out uint status);\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "vector Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset1, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset2, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset3, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset4);\n"; + + EMIT_LINE_DIRECTIVE(); + sb << "vector Gather" << componentName << "(SamplerState s, "; + sb << "float" << kBaseTextureTypes[tt].coordCount << " location, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset1, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset2, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset3, "; + sb << "int" << kBaseTextureTypes[tt].coordCount << " offset4, "; + sb << "out uint status);\n"; + } + + EMIT_LINE_DIRECTIVE(); + sb << "\n}\n"; + } } } } // Declare additional built-in generic types - + EMIT_LINE_DIRECTIVE(); sb << "__generic __magic_type(ConstantBuffer) struct ConstantBuffer {};\n"; sb << "__generic __magic_type(TextureBuffer) struct TextureBuffer {};\n"; diff --git a/source/slang/syntax.h b/source/slang/syntax.h index 66eddc536..5eb62462e 100644 --- a/source/slang/syntax.h +++ b/source/slang/syntax.h @@ -632,7 +632,7 @@ namespace Slang class Decl : public DeclBase { public: - ContainerDecl* ParentDecl; + ContainerDecl* ParentDecl = nullptr; Token Name; String const& getName() { return Name.Content; } @@ -1883,12 +1883,16 @@ namespace Slang bool isOverloaded() const { return items.Count() > 1; } }; + class SemanticsVisitor; + struct LookupRequest { - RefPtr scope = nullptr; - RefPtr endScope = nullptr; + SemanticsVisitor* semantics = nullptr; + + RefPtr scope = nullptr; + RefPtr endScope = nullptr; - LookupMask mask = LookupMask::All; + LookupMask mask = LookupMask::All; }; // An expression that references an overloaded set of declarations -- cgit v1.2.3