diff options
| -rw-r--r-- | source/slang/check.cpp | 432 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 766 | ||||
| -rw-r--r-- | source/slang/expr-defs.h | 21 | ||||
| -rw-r--r-- | source/slang/intrinsic-defs.h | 1 | ||||
| -rw-r--r-- | source/slang/lexer.cpp | 6 | ||||
| -rw-r--r-- | source/slang/lower.cpp | 234 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 192 | ||||
| -rw-r--r-- | source/slang/slang-stdlib.cpp | 356 | ||||
| -rw-r--r-- | source/slang/slang-stdlib.h | 13 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 17 | ||||
| -rw-r--r-- | source/slang/stmt-defs.h | 7 | ||||
| -rw-r--r-- | source/slang/syntax.h | 17 | ||||
| -rw-r--r-- | tests/rewriter/error0.hlsl | 16 |
13 files changed, 1271 insertions, 807 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 7c67473be..b87f7c6bc 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -243,14 +243,16 @@ namespace Slang if (lookupResult.isOverloaded()) { // We had an ambiguity anyway, so report it. - getSink()->diagnose(overloadedExpr, Diagnostics::ambiguousReference, lookupResult.items[0].declRef.GetName()); - - for(auto item : lookupResult.items) + if (!isRewriteMode()) { - String declString = getDeclSignatureString(item); - getSink()->diagnose(item.declRef, Diagnostics::overloadCandidate, declString); - } + getSink()->diagnose(overloadedExpr, Diagnostics::ambiguousReference, lookupResult.items[0].declRef.GetName()); + for(auto item : lookupResult.items) + { + String declString = getDeclSignatureString(item); + getSink()->diagnose(item.declRef, Diagnostics::overloadCandidate, declString); + } + } // TODO(tfoley): should we construct a new ErrorExpr here? overloadedExpr->Type = QualType(ExpressionType::Error); return overloadedExpr; @@ -276,9 +278,11 @@ namespace Slang return expr; } - getSink()->diagnose(expr, Diagnostics::unimplemented, "expected a type"); - // TODO: construct some kind of `ErrorExpr`? - return expr; + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::unimplemented, "expected a type"); + } + return CreateErrorExpr(expr); } RefPtr<ExpressionType> ExpectAType(RefPtr<ExpressionSyntaxNode> expr) @@ -424,7 +428,10 @@ namespace Slang { if (outProperType) { - getSink()->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter"); + if (!isRewriteMode()) + { + getSink()->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter"); + } *outProperType = ExpressionType::Error; } return false; @@ -440,7 +447,10 @@ namespace Slang { if (outProperType) { - getSink()->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter"); + if (!isRewriteMode()) + { + getSink()->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter"); + } *outProperType = ExpressionType::Error; } return false; @@ -511,7 +521,10 @@ namespace Slang if (basicType->BaseType == BaseType::Void) { // TODO(tfoley): pick the right diagnostic message - getSink()->diagnose(result.exp.Ptr(), Diagnostics::invalidTypeVoid); + if (!isRewriteMode()) + { + getSink()->diagnose(result.exp.Ptr(), Diagnostics::invalidTypeVoid); + } result.type = ExpressionType::Error; return result; } @@ -634,7 +647,9 @@ namespace Slang CASE(Int, Signed, Int32); CASE(UInt, Unsigned, Int32); CASE(UInt64, Unsigned, Int64); + CASE(Half, Float, Int16); CASE(Float, Float, Int32); + CASE(Double, Float, Int64); CASE(Void, Error, Error); #undef CASE @@ -972,7 +987,19 @@ namespace Slang RefPtr<ExpressionType> toType, RefPtr<ExpressionSyntaxNode> fromExpr) { - auto castExpr = new TypeCastExpressionSyntaxNode(); + // In "rewrite" mode, we will generate a different syntax node + // to indicate that this type-cast was implicitly generated + // by the compiler, and shouldn't appear in the output code. + RefPtr<TypeCastExpressionSyntaxNode> castExpr; + if (isRewriteMode()) + { + castExpr = new HiddenImplicitCastExpr(); + } + else + { + castExpr = new ImplicitCastExpr(); + } + castExpr->Position = fromExpr->Position; castExpr->TargetType.type = toType; castExpr->Type = QualType(toType); @@ -980,6 +1007,10 @@ namespace Slang return castExpr; } + bool isRewriteMode() + { + return (getTranslationUnit()->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING) != 0; + } // Perform type coercion, and emit errors if it isn't possible RefPtr<ExpressionSyntaxNode> Coerce( @@ -990,7 +1021,7 @@ namespace Slang // expressions without a type, and we need to ignore them. if( !fromExpr->Type.type ) { - if(getTranslationUnit()->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING ) + if(isRewriteMode()) return fromExpr; } @@ -1002,7 +1033,7 @@ namespace Slang fromExpr.Ptr(), nullptr)) { - if(!(getTranslationUnit()->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING)) + if(!isRewriteMode()) { getSink()->diagnose(fromExpr->Position, Diagnostics::typeMismatch, toType, fromExpr->Type); } @@ -1041,13 +1072,20 @@ namespace Slang else { // TODO: infer a type from the initializers + if (!initExpr) { - getSink()->diagnose(varDecl, Diagnostics::unimplemented, "variable declaration with no type must have initializer"); + if (!isRewriteMode()) + { + getSink()->diagnose(varDecl, Diagnostics::unimplemented, "variable declaration with no type must have initializer"); + } } else { - getSink()->diagnose(varDecl, Diagnostics::unimplemented, "type inference for variable declaration"); + if (!isRewriteMode()) + { + getSink()->diagnose(varDecl, Diagnostics::unimplemented, "type inference for variable declaration"); + } } } @@ -1122,7 +1160,10 @@ namespace Slang // If type expression didn't name an interface, we'll emit an error here // TODO: deal with the case of an error in the type expression (don't cascade) - getSink()->diagnose( base.exp, Diagnostics::expectedAnInterfaceGot, base.type); + if (!isRewriteMode()) + { + getSink()->diagnose( base.exp, Diagnostics::expectedAnInterfaceGot, base.type); + } } RefPtr<ConstantIntVal> checkConstantIntVal( @@ -1138,7 +1179,10 @@ namespace Slang auto constIntVal = intVal.As<ConstantIntVal>(); if(!constIntVal) { - getSink()->diagnose(expr->Position, Diagnostics::expectedIntegerConstantNotLiteral); + if (!isRewriteMode()) + { + getSink()->diagnose(expr->Position, Diagnostics::expectedIntegerConstantNotLiteral); + } return nullptr; } return constIntVal; @@ -1503,7 +1547,10 @@ namespace Slang if (!funcDecl->ReturnType.Equals(prevFuncDecl->ReturnType)) { // Bad dedeclaration - getSink()->diagnose(funcDecl, Diagnostics::unimplemented, "redeclaration has a different return type"); + if (!isRewriteMode()) + { + getSink()->diagnose(funcDecl, Diagnostics::unimplemented, "redeclaration has a different return type"); + } // Don't bother emitting other errors at this point break; @@ -1517,7 +1564,10 @@ namespace Slang if (funcDecl->Body && prevFuncDecl->Body) { // Redefinition - getSink()->diagnose(funcDecl, Diagnostics::unimplemented, "function redefinition"); + if (!isRewriteMode()) + { + getSink()->diagnose(funcDecl, Diagnostics::unimplemented, "function redefinition"); + } // Don't bother emitting other errors break; @@ -1539,7 +1589,12 @@ namespace Slang para->Type = CheckUsableType(para->Type); if (para->Type.Equals(ExpressionType::GetVoid())) - getSink()->diagnose(para, Diagnostics::parameterCannotBeVoid); + { + if (!isRewriteMode()) + { + getSink()->diagnose(para, Diagnostics::parameterCannotBeVoid); + } + } } void VisitFunctionDeclaration(FunctionSyntaxNode *functionNode) @@ -1556,7 +1611,12 @@ namespace Slang checkDecl(para); if (paraNames.Contains(para->Name.Content)) - getSink()->diagnose(para, Diagnostics::parameterAlreadyDefined, para->Name); + { + if (!isRewriteMode()) + { + getSink()->diagnose(para, Diagnostics::parameterAlreadyDefined, para->Name); + } + } else paraNames.Add(para->Name.Content); } @@ -1613,7 +1673,10 @@ namespace Slang auto outer = FindOuterStmt<BreakableStmt>(); if (!outer) { - getSink()->diagnose(stmt, Diagnostics::breakOutsideLoop); + if (!isRewriteMode()) + { + getSink()->diagnose(stmt, Diagnostics::breakOutsideLoop); + } } stmt->parentStmt = outer; } @@ -1622,7 +1685,10 @@ namespace Slang auto outer = FindOuterStmt<LoopStmt>(); if (!outer) { - getSink()->diagnose(stmt, Diagnostics::continueOutsideLoop); + if (!isRewriteMode()) + { + getSink()->diagnose(stmt, Diagnostics::continueOutsideLoop); + } } stmt->parentStmt = outer; } @@ -1691,7 +1757,10 @@ namespace Slang if (!switchStmt) { - getSink()->diagnose(stmt, Diagnostics::caseOutsideSwitch); + if (!isRewriteMode()) + { + getSink()->diagnose(stmt, Diagnostics::caseOutsideSwitch); + } } else { @@ -1707,7 +1776,10 @@ namespace Slang auto switchStmt = FindOuterStmt<SwitchStmt>(); if (!switchStmt) { - getSink()->diagnose(stmt, Diagnostics::defaultOutsideSwitch); + if (!isRewriteMode()) + { + getSink()->diagnose(stmt, Diagnostics::defaultOutsideSwitch); + } } stmt->parentStmt = switchStmt; } @@ -1723,7 +1795,6 @@ namespace Slang // Nothing to do } - void visitEmptyStatementSyntaxNode(EmptyStatementSyntaxNode*) { // Nothing to do @@ -1739,7 +1810,12 @@ namespace Slang if (!stmt->Expression) { if (function && !function->ReturnType.Equals(ExpressionType::GetVoid())) - getSink()->diagnose(stmt, Diagnostics::returnNeedsExpression); + { + if (!isRewriteMode()) + { + getSink()->diagnose(stmt, Diagnostics::returnNeedsExpression); + } + } } else { @@ -1832,7 +1908,10 @@ namespace Slang // TODO(tfoley): How to handle the case where bound isn't known? if (GetMinBound(elementCount) <= 0) { - getSink()->diagnose(varDecl, Diagnostics::invalidArraySize); + if (!isRewriteMode()) + { + getSink()->diagnose(varDecl, Diagnostics::invalidArraySize); + } return; } } @@ -1853,7 +1932,12 @@ namespace Slang #endif varDecl->Type = typeExp; if (varDecl->Type.Equals(ExpressionType::GetVoid())) - getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid); + { + if (!isRewriteMode()) + { + getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid); + } + } if(auto initExpr = varDecl->Expr) { @@ -2044,6 +2128,12 @@ namespace Slang RefPtr<IntVal> TryConstantFoldExpr( ExpressionSyntaxNode* expr) { + // Unwrap any "identity" expressions + while (auto parenExpr = dynamic_cast<ParenExpr*>(expr)) + { + expr = parenExpr->base; + } + // TODO(tfoley): more serious constant folding here if (auto constExp = dynamic_cast<ConstantExpressionSyntaxNode*>(expr)) { @@ -2160,7 +2250,10 @@ namespace Slang auto result = TryCheckIntegerConstantExpression(expr.Ptr()); if (!result) { - getSink()->diagnose(expr, Diagnostics::expectedIntegerConstantNotConstant); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::expectedIntegerConstantNotConstant); + } } return result; } @@ -2177,7 +2270,10 @@ namespace Slang if (!indexExpr->Type->Equals(ExpressionType::GetInt()) && !indexExpr->Type->Equals(ExpressionType::GetUInt())) { - getSink()->diagnose(indexExpr, Diagnostics::subscriptIndexNonInteger); + if (!isRewriteMode()) + { + getSink()->diagnose(indexExpr, Diagnostics::subscriptIndexNonInteger); + } return CreateErrorExpr(subscriptExpr.Ptr()); } @@ -2320,7 +2416,10 @@ namespace Slang fail: { - getSink()->diagnose(subscriptExpr, Diagnostics::subscriptNonArray, baseType); + if (!isRewriteMode()) + { + getSink()->diagnose(subscriptExpr, Diagnostics::subscriptNonArray, baseType); + } return CreateErrorExpr(subscriptExpr); } } @@ -2419,6 +2518,16 @@ namespace Slang return appExpr; } + RefPtr<ExpressionSyntaxNode> visitParenExpr(ParenExpr* expr) + { + auto base = expr->base; + base = CheckTerm(base); + + expr->base = base; + expr->Type = base->Type; + return expr; + } + // RefPtr<ExpressionSyntaxNode> visitAssignExpr(AssignExpr* expr) @@ -2431,7 +2540,10 @@ namespace Slang if (!type.IsLeftValue) { - getSink()->diagnose(expr, Diagnostics::assignNonLValue); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::assignNonLValue); + } } expr->Type = type; return expr; @@ -2460,7 +2572,10 @@ namespace Slang } else { - getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "expected a nominal type here"); + if (!isRewriteMode()) + { + getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "expected a nominal type here"); + } } } else if (decl->targetType->Equals(ExpressionType::Error)) @@ -2469,7 +2584,10 @@ namespace Slang } else { - getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "expected a nominal type here"); + if (!isRewriteMode()) + { + getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "expected a nominal type here"); + } } decl->SetCheckState(DeclCheckState::CheckedHeader); @@ -3073,12 +3191,18 @@ namespace Slang { if (argCount < paramCounts.required) { - getSink()->diagnose(context.appExpr, Diagnostics::notEnoughArguments, argCount, paramCounts.required); + if (!isRewriteMode()) + { + getSink()->diagnose(context.appExpr, Diagnostics::notEnoughArguments, argCount, paramCounts.required); + } } else { assert(argCount > paramCounts.allowed); - getSink()->diagnose(context.appExpr, Diagnostics::tooManyArguments, argCount, paramCounts.allowed); + if (!isRewriteMode()) + { + getSink()->diagnose(context.appExpr, Diagnostics::tooManyArguments, argCount, paramCounts.allowed); + } } } @@ -3100,8 +3224,11 @@ namespace Slang if (context.mode != OverloadResolveContext::Mode::JustTrying) { - getSink()->diagnose(context.appExpr, Diagnostics::expectedPrefixOperator); - getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName()); + if (!isRewriteMode()) + { + getSink()->diagnose(context.appExpr, Diagnostics::expectedPrefixOperator); + getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName()); + } } return false; @@ -3113,8 +3240,11 @@ namespace Slang if (context.mode != OverloadResolveContext::Mode::JustTrying) { - getSink()->diagnose(context.appExpr, Diagnostics::expectedPostfixOperator); - getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName()); + if (!isRewriteMode()) + { + getSink()->diagnose(context.appExpr, Diagnostics::expectedPostfixOperator); + getSink()->diagnose(decl, Diagnostics::seeDefinitionOf, decl->getName()); + } } return false; @@ -3311,13 +3441,16 @@ namespace Slang if (candidate.status == OverloadCandidate::Status::GenericArgumentInferenceFailed) { String callString = GetCallSignatureString(context.appExpr); - getSink()->diagnose( - context.appExpr, - Diagnostics::genericArgumentInferenceFailed, - callString); + if (!isRewriteMode()) + { + getSink()->diagnose( + context.appExpr, + Diagnostics::genericArgumentInferenceFailed, + callString); - String declString = getDeclSignatureString(candidate.item); - getSink()->diagnose(candidate.item.declRef, Diagnostics::genericSignatureTried, declString); + String declString = getDeclSignatureString(candidate.item); + getSink()->diagnose(candidate.item.declRef, Diagnostics::genericSignatureTried, declString); + } goto error; } @@ -4223,6 +4356,14 @@ namespace Slang { return CreateErrorExpr(expr); } + // If any of the arguments is an error, then we should bail out, to avoid + // cascading errors where we successfully pick an overload, but not the one + // the user meant. + for (auto arg : expr->Arguments) + { + if (IsErrorExpr(arg)) + return CreateErrorExpr(expr); + } OverloadResolveContext context; context.appExpr = expr; @@ -4268,11 +4409,17 @@ namespace Slang // We will construct a diagnostic message to help out. if (funcName.Length() != 0) { - getSink()->diagnose(expr, Diagnostics::noApplicableOverloadForNameWithArgs, funcName, argsList); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::noApplicableOverloadForNameWithArgs, funcName, argsList); + } } else { - getSink()->diagnose(expr, Diagnostics::noApplicableWithArgs, argsList); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::noApplicableWithArgs, argsList); + } } } else @@ -4281,32 +4428,41 @@ namespace Slang if (funcName.Length() != 0) { - getSink()->diagnose(expr, Diagnostics::ambiguousOverloadForNameWithArgs, funcName, argsList); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::ambiguousOverloadForNameWithArgs, funcName, argsList); + } } else { - getSink()->diagnose(expr, Diagnostics::ambiguousOverloadWithArgs, argsList); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::ambiguousOverloadWithArgs, argsList); + } } } - UInt candidateCount = context.bestCandidates.Count(); - UInt maxCandidatesToPrint = 10; // don't show too many candidates at once... - UInt candidateIndex = 0; - for (auto candidate : context.bestCandidates) + if (!isRewriteMode()) { - String declString = getDeclSignatureString(candidate.item); + UInt candidateCount = context.bestCandidates.Count(); + UInt maxCandidatesToPrint = 10; // don't show too many candidates at once... + UInt candidateIndex = 0; + for (auto candidate : context.bestCandidates) + { + String declString = getDeclSignatureString(candidate.item); - declString = declString + "[" + String(candidate.conversionCostSum) + "]"; + declString = declString + "[" + String(candidate.conversionCostSum) + "]"; - getSink()->diagnose(candidate.item.declRef, Diagnostics::overloadCandidate, declString); + getSink()->diagnose(candidate.item.declRef, Diagnostics::overloadCandidate, declString); - candidateIndex++; - if (candidateIndex == maxCandidatesToPrint) - break; - } - if (candidateIndex != candidateCount) - { - getSink()->diagnose(expr, Diagnostics::moreOverloadCandidates, candidateCount - candidateIndex); + candidateIndex++; + if (candidateIndex == maxCandidatesToPrint) + break; + } + if (candidateIndex != candidateCount) + { + getSink()->diagnose(expr, Diagnostics::moreOverloadCandidates, candidateCount - candidateIndex); + } } return CreateErrorExpr(expr); @@ -4322,7 +4478,10 @@ namespace Slang else { // Nothing at all was found that we could even consider invoking - getSink()->diagnose(expr->FunctionExpr, Diagnostics::expectedFunction); + if (!isRewriteMode()) + { + getSink()->diagnose(expr->FunctionExpr, Diagnostics::expectedFunction); + } expr->Type = QualType(ExpressionType::Error); return expr; } @@ -4417,7 +4576,10 @@ namespace Slang // TODO(tfoley): print a reasonable message here... - getSink()->diagnose(genericAppExpr, Diagnostics::unimplemented, "no applicable generic"); + if (!isRewriteMode()) + { + getSink()->diagnose(genericAppExpr, Diagnostics::unimplemented, "no applicable generic"); + } return CreateErrorExpr(genericAppExpr); } @@ -4448,76 +4610,12 @@ namespace Slang else { // Nothing at all was found that we could even consider invoking - getSink()->diagnose(genericAppExpr, Diagnostics::unimplemented, "expected a generic"); - return CreateErrorExpr(genericAppExpr); - } - - -#if TIMREMOVED - - if (IsErrorExpr(base)) - { - return CreateErrorExpr(typeNode); - } - else if(auto baseDeclRefExpr = base.As<DeclRefExpr>()) - { - auto declRef = baseDeclRefExpr->declRef; - - if (auto genericDeclRef = declRef.As<GenericDecl>()) + if (!isRewriteMode()) { - int argCount = typeNode->Args.Count(); - int argIndex = 0; - for (RefPtr<Decl> member : genericDeclRef.getDecl()->Members) - { - if (auto typeParam = member.As<GenericTypeParamDecl>()) - { - if (argIndex == argCount) - { - // Too few arguments! - - } - - // TODO: checking! - } - else if (auto valParam = member.As<GenericValueParamDecl>()) - { - // TODO: checking - } - else - { - - } - } - if (argIndex != argCount) - { - // Too many arguments! - } - - // Now instantiate the declaration given those arguments - auto type = InstantiateGenericType(genericDeclRef, args); - typeResult = type; - typeNode->Type = new TypeExpressionType(type); - return typeNode; - } - } - else if (auto overloadedExpr = base.As<OverloadedExpr>()) - { - // We are referring to a bunch of declarations, each of which might be generic - LookupResult result; - for (auto item : overloadedExpr->lookupResult2.items) - { - auto applied = TryApplyGeneric(item, typeNode); - if (!applied) - continue; - - AddToLookupResult(result, appliedItem); + getSink()->diagnose(genericAppExpr, Diagnostics::unimplemented, "expected a generic"); } + return CreateErrorExpr(genericAppExpr); } - - // TODO: correct diagnostic here! - getSink()->diagnose(typeNode, Diagnostics::expectedAGeneric, base->Type); - return CreateErrorExpr(typeNode); -#endif } RefPtr<ExpressionSyntaxNode> visitSharedTypeExpr(SharedTypeExpr* expr) @@ -4568,7 +4666,10 @@ namespace Slang if (i < expr->Arguments.Count() && expr->Arguments[i]->Type->AsBasicType() && !expr->Arguments[i]->Type.IsLeftValue) { - getSink()->diagnose(expr->Arguments[i], Diagnostics::argumentExpectedLValue, (*params)[i]->Name); + if (!isRewriteMode()) + { + getSink()->diagnose(expr->Arguments[i], Diagnostics::argumentExpectedLValue, (*params)[i]->Name); + } } } } @@ -4610,7 +4711,10 @@ namespace Slang expr); } - getSink()->diagnose(expr, Diagnostics::undefinedIdentifier2, expr->name); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::undefinedIdentifier2, expr->name); + } return expr; } @@ -4651,32 +4755,13 @@ namespace Slang fail: // Default: in no other case succeds, then the cast failed and we emit a diagnostic. - getSink()->diagnose(expr, Diagnostics::invalidTypeCast, expr->Expression->Type, targetType->ToString()); - expr->Type = QualType(ExpressionType::Error); - return expr; - } -#if TIMREMOVED - virtual RefPtr<ExpressionSyntaxNode> VisitSelectExpression(SelectExpressionSyntaxNode * expr) override - { - auto selectorExpr = expr->SelectorExpr; - selectorExpr = CheckExpr(selectorExpr); - selectorExpr = Coerce(ExpressionType::GetBool(), selectorExpr); - expr->SelectorExpr = selectorExpr; - - // TODO(tfoley): We need a general purpose "join" on types for inferring - // generic argument types for builtins/intrinsics, so this should really - // be using the exact same logic... - // - expr->Expr0 = expr->Expr0->Accept(this).As<ExpressionSyntaxNode>(); - expr->Expr1 = expr->Expr1->Accept(this).As<ExpressionSyntaxNode>(); - if (!expr->Expr0->Type->Equals(expr->Expr1->Type.Ptr())) + if (!isRewriteMode()) { - getSink()->diagnose(expr, Diagnostics::selectValuesTypeMismatch); + getSink()->diagnose(expr, Diagnostics::invalidTypeCast, expr->Expression->Type, targetType->ToString()); } - expr->Type = expr->Expr0->Type; + expr->Type = QualType(ExpressionType::Error); return expr; } -#endif // Get the type to use when referencing a declaration QualType GetTypeForDeclRef(DeclRef<Decl> declRef) @@ -4771,7 +4856,10 @@ namespace Slang case 'w': case 'a': elementIndex = 3; break; default: // An invalid character in the swizzle is an error - getSink()->diagnose(swizExpr, Diagnostics::unimplemented, "invalid component name for swizzle"); + if (!isRewriteMode()) + { + getSink()->diagnose(swizExpr, Diagnostics::unimplemented, "invalid component name for swizzle"); + } anyError = true; continue; } @@ -4782,7 +4870,10 @@ namespace Slang // Make sure the index is in range for the source type if (elementIndex >= limitElement) { - getSink()->diagnose(swizExpr, Diagnostics::unimplemented, "swizzle component out of range for type"); + if (!isRewriteMode()) + { + getSink()->diagnose(swizExpr, Diagnostics::unimplemented, "swizzle component out of range for type"); + } anyError = true; continue; } @@ -4806,7 +4897,7 @@ namespace Slang if (anyError) { - swizExpr->Type = QualType(ExpressionType::Error); + return CreateErrorExpr(memberRefExpr); } else if (elementCount == 1) { @@ -4844,7 +4935,10 @@ namespace Slang } else { - getSink()->diagnose(memberRefExpr, Diagnostics::unimplemented, "swizzle on vector of unknown size"); + if (!isRewriteMode()) + { + getSink()->diagnose(memberRefExpr, Diagnostics::unimplemented, "swizzle on vector of unknown size"); + } return CreateErrorExpr(memberRefExpr); } } @@ -4928,7 +5022,10 @@ namespace Slang // catch-all fail: - getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->name, baseType); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->name, baseType); + } expr->Type = QualType(ExpressionType::Error); return expr; } @@ -4940,7 +5037,10 @@ namespace Slang if (!baseType->Equals(ExpressionType::Error.Ptr()) && expr->Type->Equals(ExpressionType::Error.Ptr())) { - getSink()->diagnose(expr, Diagnostics::typeHasNoPublicMemberOfName, baseType, expr->name); + if (!isRewriteMode()) + { + getSink()->diagnose(expr, Diagnostics::typeHasNoPublicMemberOfName, baseType, expr->name); + } } return expr; } diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index df74d19e4..c135090c1 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -21,9 +21,6 @@ struct SharedEmitContext // The target language we want to generate code for CodeGenTarget target; - // A set of words reserved by the target - Dictionary<String, String> reservedWords; - // The string of code we've built so far StringBuilder sb; @@ -57,10 +54,6 @@ struct EmitContext { // The shared context that is in effect SharedEmitContext* shared; - - // Are we in "rewrite" mode, where we are trying to reproduce the input - // code as closely as posible? - bool isRewrite; }; // @@ -84,6 +77,194 @@ static String getStringOrIdentifierTokenValue( } +enum EPrecedence +{ +#define LEFT(NAME) \ + kEPrecedence_##NAME##_Left, \ + kEPrecedence_##NAME##_Right + +#define RIGHT(NAME) \ + kEPrecedence_##NAME##_Right, \ + kEPrecedence_##NAME##_Left + +#define NONASSOC(NAME) \ + kEPrecedence_##NAME##_Left, \ + kEPrecedence_##NAME##_Right = kEPrecedence_##NAME##_Left + + NONASSOC(None), + LEFT(Comma), + + NONASSOC(General), + + RIGHT(Assign), + + RIGHT(Conditional), + + LEFT(Or), + LEFT(And), + LEFT(BitOr), + LEFT(BitXor), + LEFT(BitAnd), + + LEFT(Equality), + LEFT(Relational), + LEFT(Shift), + LEFT(Additive), + LEFT(Multiplicative), + RIGHT(Prefix), + LEFT(Postfix), + NONASSOC(Atomic), + +#if 0 + + kEPrecedence_None, + kEPrecedence_Comma, + + kEPrecedence_Assign, + kEPrecedence_AddAssign = kEPrecedence_Assign, + kEPrecedence_SubAssign = kEPrecedence_Assign, + kEPrecedence_MulAssign = kEPrecedence_Assign, + kEPrecedence_DivAssign = kEPrecedence_Assign, + kEPrecedence_ModAssign = kEPrecedence_Assign, + kEPrecedence_LshAssign = kEPrecedence_Assign, + kEPrecedence_RshAssign = kEPrecedence_Assign, + kEPrecedence_OrAssign = kEPrecedence_Assign, + kEPrecedence_AndAssign = kEPrecedence_Assign, + kEPrecedence_XorAssign = kEPrecedence_Assign, + + kEPrecedence_General = kEPrecedence_Assign, + + kEPrecedence_Conditional, // "ternary" + kEPrecedence_Or, + kEPrecedence_And, + kEPrecedence_BitOr, + kEPrecedence_BitXor, + kEPrecedence_BitAnd, + + kEPrecedence_Eql, + kEPrecedence_Neq = kEPrecedence_Eql, + + kEPrecedence_Less, + kEPrecedence_Greater = kEPrecedence_Less, + kEPrecedence_Leq = kEPrecedence_Less, + kEPrecedence_Geq = kEPrecedence_Less, + + kEPrecedence_Lsh, + kEPrecedence_Rsh = kEPrecedence_Lsh, + + kEPrecedence_Add, + kEPrecedence_Sub = kEPrecedence_Add, + + kEPrecedence_Mul, + kEPrecedence_Div = kEPrecedence_Mul, + kEPrecedence_Mod = kEPrecedence_Mul, + + kEPrecedence_Prefix, + kEPrecedence_Postfix, + kEPrecedence_Atomic = kEPrecedence_Postfix + +#endif + +}; + +// Info on an op for emit purposes +struct EOpInfo +{ + char const* op; + EPrecedence leftPrecedence; + EPrecedence rightPrecedence; +}; + +#define OP(NAME, TEXT, PREC) \ +static const EOpInfo kEOp_##NAME = { TEXT, kEPrecedence_##PREC##_Left, kEPrecedence_##PREC##_Right, } + +OP(None, "", None); + +OP(Comma, ",", Comma); + +OP(General, "", General); + +OP(Assign, "=", Assign); +OP(AddAssign, "+=", Assign); +OP(SubAssign, "-=", Assign); +OP(MulAssign, "*=", Assign); +OP(DivAssign, "/=", Assign); +OP(ModAssign, "%=", Assign); +OP(LshAssign, "<<=", Assign); +OP(RshAssign, ">>=", Assign); +OP(OrAssign, "|=", Assign); +OP(AndAssign, "&=", Assign); +OP(XorAssign, "^=", Assign); + +OP(Conditional, "?:", Conditional); + +OP(Or, "||", Or); +OP(And, "&&", And); +OP(BitOr, "|", BitOr); +OP(BitXor, "^", BitXor); +OP(BitAnd, "&", BitAnd); + +OP(Eql, "==", Equality); +OP(Neq, "!=", Equality); + +OP(Less, "<", Relational); +OP(Greater, ">", Relational); +OP(Leq, "<=", Relational); +OP(Geq, ">=", Relational); + +OP(Lsh, "<<", Shift); +OP(Rsh, ">>", Shift); + +OP(Add, "+", Additive); +OP(Sub, "-", Additive); + +OP(Mul, "*", Multiplicative); +OP(Div, "/", Multiplicative); +OP(Mod, "%", Multiplicative); + +OP(Prefix, "", Prefix); +OP(Postfix, "", Postfix); +OP(Atomic, "", Atomic); + +#undef OP + +// Table to allow data-driven lookup of an op based on its +// name (to assist when outputting unchecked operator calls) +static EOpInfo const* const kInfixOpInfos[] = +{ + &kEOp_Comma, + &kEOp_Assign, + &kEOp_AddAssign, + &kEOp_SubAssign, + &kEOp_MulAssign, + &kEOp_DivAssign, + &kEOp_ModAssign, + &kEOp_LshAssign, + &kEOp_RshAssign, + &kEOp_OrAssign, + &kEOp_AndAssign, + &kEOp_XorAssign, + &kEOp_Or, + &kEOp_And, + &kEOp_BitOr, + &kEOp_BitXor, + &kEOp_BitAnd, + &kEOp_Eql, + &kEOp_Neq, + &kEOp_Less, + &kEOp_Greater, + &kEOp_Leq, + &kEOp_Geq, + &kEOp_Lsh, + &kEOp_Rsh, + &kEOp_Add, + &kEOp_Sub, + &kEOp_Mul, + &kEOp_Div, + &kEOp_Mod, +}; + + // // represents a declarator for use in emitting types @@ -113,7 +294,7 @@ struct TypeEmitArg struct ExprEmitArg { - int outerPrec; + EOpInfo outerPrec; }; struct DeclEmitArg @@ -201,46 +382,12 @@ struct EmitVisitor Emit(text.begin(), text.end()); } - bool isReservedWord(String const& name) - { - return context->shared->reservedWords.TryGetValue(name) != nullptr; - } - void emitName( String const& inName, CodePosition const& loc) { String name = inName; - // By default, we would like to emit a name in the generated - // code exactly as it appeared in the soriginal program. - // When that isn't possible, we'd like to emit a name as - // close to the original as possible (to ensure that existing - // debugging tools still work reasonably well). - // - // One reason why a name might not be allowed as-is is that - // it could collide with a reserved word in the target language. - // Another reason is that it might not follow a naming convention - // imposed by the target (e.g., in GLSL names starting with - // `gl_` or containing `__` are reserved). - // - // Given a name that should not be allowed, we want to - // change it to a name that *is* allowed. e.g., by adding - // `_` to the end of a reserved word. - // - // The next problem this creates is that the modified name - // could not collide with an existing use of the same - // (valid) name. - // - // For now we are going to solve this problem in a simple - // and ad hoc fashion, but longer term we'll want to do - // something sytematic. - - if (isReservedWord(name)) - { - name = name + "_"; - } - advanceToSourceLocation(loc); emit(name); } @@ -970,6 +1117,14 @@ struct EmitVisitor { e = derefExpr->base; } + + if (auto declRefExpr = e.As<DeclRefExpr>()) + { + auto decl = declRefExpr->declRef.getDecl(); + if (decl && decl->HasModifier<TransparentModifier>()) + return true; + } + // Is the expression referencing a constant buffer? if (auto cbufferType = e->Type->As<ConstantBufferType>()) { @@ -979,73 +1134,30 @@ struct EmitVisitor return false; } - enum - { - kPrecedence_None, - kPrecedence_Comma, - - kPrecedence_Assign, - kPrecedence_AddAssign = kPrecedence_Assign, - kPrecedence_SubAssign = kPrecedence_Assign, - kPrecedence_MulAssign = kPrecedence_Assign, - kPrecedence_DivAssign = kPrecedence_Assign, - kPrecedence_ModAssign = kPrecedence_Assign, - kPrecedence_LshAssign = kPrecedence_Assign, - kPrecedence_RshAssign = kPrecedence_Assign, - kPrecedence_OrAssign = kPrecedence_Assign, - kPrecedence_AndAssign = kPrecedence_Assign, - kPrecedence_XorAssign = kPrecedence_Assign, - - kPrecedence_General = kPrecedence_Assign, - - kPrecedence_Conditional, // "ternary" - kPrecedence_Or, - kPrecedence_And, - kPrecedence_BitOr, - kPrecedence_BitXor, - kPrecedence_BitAnd, - - kPrecedence_Eql, - kPrecedence_Neq = kPrecedence_Eql, - - kPrecedence_Less, - kPrecedence_Greater = kPrecedence_Less, - kPrecedence_Leq = kPrecedence_Less, - kPrecedence_Geq = kPrecedence_Less, - - kPrecedence_Lsh, - kPrecedence_Rsh = kPrecedence_Lsh, - - kPrecedence_Add, - kPrecedence_Sub = kPrecedence_Add, - - kPrecedence_Mul, - kPrecedence_Div = kPrecedence_Mul, - kPrecedence_Mod = kPrecedence_Mul, - - kPrecedence_Prefix, - kPrecedence_Postfix, - kPrecedence_Atomic = kPrecedence_Postfix - }; - +#if 0 void EmitPostfixExpr(RefPtr<ExpressionSyntaxNode> expr) { - EmitExprWithPrecedence(expr, kPrecedence_Postfix); + EmitExprWithPrecedence(expr, kEOp_Postfix); } +#endif void EmitExpr(RefPtr<ExpressionSyntaxNode> expr) { - EmitExprWithPrecedence(expr, kPrecedence_General); + EmitExprWithPrecedence(expr, kEOp_General); } - bool MaybeEmitParens(int outerPrec, int prec) + bool MaybeEmitParens(EOpInfo& outerPrec, EOpInfo prec) { - if (prec <= outerPrec) + bool needParens = (prec.leftPrecedence <= outerPrec.leftPrecedence) + || (prec.rightPrecedence <= outerPrec.rightPrecedence); + + if (needParens) { Emit("("); - return true; + + outerPrec = kEOp_None; } - return false; + return needParens; } // When we are going to emit an expression in an l-value context, @@ -1071,8 +1183,8 @@ struct EmitVisitor } void emitInfixExprImpl( - int outerPrec, - int prec, + EOpInfo outerPrec, + EOpInfo prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr, bool isAssign) @@ -1085,30 +1197,30 @@ struct EmitVisitor left = prepareLValueExpr(left); } - EmitExprWithPrecedence(left, prec); + EmitExprWithPrecedence(left, leftSide(outerPrec, prec)); Emit(" "); Emit(op); Emit(" "); - EmitExprWithPrecedence(binExpr->Arguments[1], prec); + EmitExprWithPrecedence(binExpr->Arguments[1], rightSide(prec, outerPrec)); if (needsClose) { Emit(")"); } } - void EmitBinExpr(int outerPrec, int prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr) + void EmitBinExpr(EOpInfo outerPrec, EOpInfo prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr) { emitInfixExprImpl(outerPrec, prec, op, binExpr, false); } - void EmitBinAssignExpr(int outerPrec, int prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr) + void EmitBinAssignExpr(EOpInfo outerPrec, EOpInfo prec, char const* op, RefPtr<InvokeExpressionSyntaxNode> binExpr) { emitInfixExprImpl(outerPrec, prec, op, binExpr, true); } void emitUnaryExprImpl( - int outerPrec, - int prec, + EOpInfo outerPrec, + EOpInfo prec, char const* preOp, char const* postOp, RefPtr<InvokeExpressionSyntaxNode> expr, @@ -1123,7 +1235,16 @@ struct EmitVisitor arg = prepareLValueExpr(arg); } - EmitExprWithPrecedence(arg, prec); + if (preOp) + { + EmitExprWithPrecedence(arg, rightSide(prec, outerPrec)); + } + else + { + assert(postOp); + EmitExprWithPrecedence(arg, leftSide(outerPrec, prec)); + } + Emit(postOp); if (needsClose) { @@ -1132,8 +1253,8 @@ struct EmitVisitor } void EmitUnaryExpr( - int outerPrec, - int prec, + EOpInfo outerPrec, + EOpInfo prec, char const* preOp, char const* postOp, RefPtr<InvokeExpressionSyntaxNode> expr) @@ -1142,8 +1263,8 @@ struct EmitVisitor } void EmitUnaryAssignExpr( - int outerPrec, - int prec, + EOpInfo outerPrec, + EOpInfo prec, char const* preOp, char const* postOp, RefPtr<InvokeExpressionSyntaxNode> expr) @@ -1206,9 +1327,10 @@ struct EmitVisitor // just an expression of the form `f(a0, a1, ...)` void emitSimpleCallExpr( RefPtr<InvokeExpressionSyntaxNode> callExpr, - int outerPrec) + EOpInfo outerPrec) { - bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Postfix); + auto prec = kEOp_Postfix; + bool needClose = MaybeEmitParens(outerPrec, prec); auto funcExpr = callExpr->FunctionExpr; if (auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>()) @@ -1222,13 +1344,13 @@ struct EmitVisitor else { // default case: just emit the decl ref - EmitExpr(funcExpr); + EmitExprWithPrecedence(funcExpr, leftSide(outerPrec, prec)); } } else { // default case: just emit the expression - EmitPostfixExpr(funcExpr); + EmitExprWithPrecedence(funcExpr, leftSide(outerPrec, prec)); } Emit("("); @@ -1273,7 +1395,23 @@ struct EmitVisitor emit("\""); } - void EmitExprWithPrecedence(RefPtr<ExpressionSyntaxNode> expr, int outerPrec) + EOpInfo leftSide(EOpInfo const& outerPrec, EOpInfo const& prec) + { + EOpInfo result; + result.leftPrecedence = outerPrec.leftPrecedence; + result.rightPrecedence = prec.leftPrecedence; + return result; + } + + EOpInfo rightSide(EOpInfo const& prec, EOpInfo const& outerPrec) + { + EOpInfo result; + result.leftPrecedence = prec.rightPrecedence; + result.rightPrecedence = outerPrec.rightPrecedence; + return result; + } + + void EmitExprWithPrecedence(RefPtr<ExpressionSyntaxNode> expr, EOpInfo outerPrec) { ExprEmitArg arg; arg.outerPrec = outerPrec; @@ -1281,6 +1419,14 @@ struct EmitVisitor ExprVisitorWithArg::dispatch(expr, arg); } + void EmitExprWithPrecedence(RefPtr<ExpressionSyntaxNode> expr, EPrecedence leftPrec, EPrecedence rightPrec) + { + EOpInfo outerPrec; + outerPrec.leftPrecedence = leftPrec; + outerPrec.rightPrecedence = rightPrec; + } + + #define UNEXPECTED(NAME) \ void visit##NAME(NAME*, ExprEmitArg const&) \ { Emit(#NAME); } @@ -1289,35 +1435,113 @@ struct EmitVisitor #undef UNEXPECTED - void visitSharedTypeExpr(SharedTypeExpr* expr, ExprEmitArg const& arg) + void visitSharedTypeExpr(SharedTypeExpr* expr, ExprEmitArg const&) { emitTypeExp(expr->base); } void visitSelectExpressionSyntaxNode(SelectExpressionSyntaxNode* selectExpr, ExprEmitArg const& arg) { + auto prec = kEOp_Conditional; auto outerPrec = arg.outerPrec; - bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Conditional); + bool needClose = MaybeEmitParens(outerPrec, kEOp_Conditional); - EmitExprWithPrecedence(selectExpr->Arguments[0], kPrecedence_Conditional); + // TODO(tfoley): Need to ver the precedence here... + + EmitExprWithPrecedence(selectExpr->Arguments[0], leftSide(outerPrec, prec)); Emit(" ? "); - EmitExprWithPrecedence(selectExpr->Arguments[1], kPrecedence_Conditional); + EmitExprWithPrecedence(selectExpr->Arguments[1], prec); Emit(" : "); - EmitExprWithPrecedence(selectExpr->Arguments[2], kPrecedence_Conditional); + EmitExprWithPrecedence(selectExpr->Arguments[2], rightSide(prec, outerPrec)); if(needClose) Emit(")"); } + void visitParenExpr(ParenExpr* expr, ExprEmitArg const&) + { + Emit("("); + EmitExprWithPrecedence(expr->base, kEOp_None); + Emit(")"); + } + void visitAssignExpr(AssignExpr* assignExpr, ExprEmitArg const& arg) { + auto prec = kEOp_Assign; auto outerPrec = arg.outerPrec; - bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Assign); - EmitExprWithPrecedence(assignExpr->left, kPrecedence_Assign); + bool needClose = MaybeEmitParens(outerPrec, prec); + EmitExprWithPrecedence(assignExpr->left, leftSide(outerPrec, prec)); Emit(" = "); - EmitExprWithPrecedence(assignExpr->right, kPrecedence_Assign); + EmitExprWithPrecedence(assignExpr->right, rightSide(prec, outerPrec)); if(needClose) Emit(")"); } + void emitUncheckedCallExpr( + RefPtr<InvokeExpressionSyntaxNode> callExpr, + String const& funcName, + ExprEmitArg const& arg) + { + auto outerPrec = arg.outerPrec; + auto funcExpr = callExpr->FunctionExpr; + + // This can occur when we are dealing with unchecked input syntax, + // because we are in "rewriter" mode. In this case we should go + // ahead and emit things in the form that they were written. + if( auto infixExpr = callExpr.As<InfixExpr>() ) + { + auto prec = kEOp_Comma; + for (auto opInfo : kInfixOpInfos) + { + if (funcName == opInfo->op) + { + prec = *opInfo; + break; + } + } + + EmitBinExpr( + outerPrec, + prec, + funcName.Buffer(), + callExpr); + } + else if( auto prefixExpr = callExpr.As<PrefixExpr>() ) + { + EmitUnaryExpr( + outerPrec, + kEOp_Prefix, + funcName.Buffer(), + "", + callExpr); + } + else if(auto postfixExpr = callExpr.As<PostfixExpr>()) + { + EmitUnaryExpr( + outerPrec, + kEOp_Postfix, + "", + funcName.Buffer(), + callExpr); + } + else + { + bool needClose = MaybeEmitParens(outerPrec, kEOp_Postfix); + + EmitExpr(funcExpr); + + Emit("("); + UInt argCount = callExpr->Arguments.Count(); + for (UInt aa = 0; aa < argCount; ++aa) + { + if (aa != 0) Emit(", "); + EmitExpr(callExpr->Arguments[aa]); + } + Emit(")"); + + if (needClose) Emit(")"); + } + } + + void visitInvokeExpressionSyntaxNode( RefPtr<InvokeExpressionSyntaxNode> callExpr, ExprEmitArg const& arg) @@ -1331,46 +1555,14 @@ struct EmitVisitor auto funcDecl = funcDeclRef.getDecl(); if(!funcDecl) { - // This can occur when we are dealing with unchecked input syntax, - // because we are in "rewriter" mode. In this case we should go - // ahead and emit things in the form that they were written. - if( auto infixExpr = callExpr.As<InfixExpr>() ) - { - EmitBinExpr( - outerPrec, - kPrecedence_Comma, - funcDeclRefExpr->name.Buffer(), - callExpr); - } - else if( auto prefixExpr = callExpr.As<PrefixExpr>() ) - { - EmitUnaryExpr( - outerPrec, - kPrecedence_Prefix, - funcDeclRefExpr->name.Buffer(), - "", - callExpr); - } - else if(auto postfixExpr = callExpr.As<PostfixExpr>()) - { - EmitUnaryExpr( - outerPrec, - kPrecedence_Postfix, - "", - funcDeclRefExpr->name.Buffer(), - callExpr); - } - else - { - emitSimpleCallExpr(callExpr, outerPrec); - } + emitUncheckedCallExpr(callExpr, funcDeclRef.GetName(), arg); return; } else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>()) { switch (intrinsicOpModifier->op) { - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(outerPrec, kPrecedence_##NAME, #OP, callExpr); return + #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return CASE(Mul, *); CASE(Div, / ); CASE(Mod, %); @@ -1391,7 +1583,7 @@ struct EmitVisitor CASE(Or, || ); #undef CASE - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinAssignExpr(outerPrec, kPrecedence_##NAME, #OP, callExpr); return + #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinAssignExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return CASE(Assign, =); CASE(AddAssign, +=); CASE(SubAssign, -=); @@ -1405,20 +1597,21 @@ struct EmitVisitor CASE(XorAssign, ^=); #undef CASE - case IntrinsicOp::Sequence: EmitBinExpr(outerPrec, kPrecedence_Comma, ",", callExpr); return; + case IntrinsicOp::Sequence: EmitBinExpr(outerPrec, kEOp_Comma, ",", callExpr); return; - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryExpr(outerPrec, kPrecedence_Prefix, #OP, "", callExpr); return + #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return + CASE(Pos, +); CASE(Neg, -); CASE(Not, !); CASE(BitNot, ~); #undef CASE - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kPrecedence_Prefix, #OP, "", callExpr); return + #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return CASE(PreInc, ++); CASE(PreDec, --); #undef CASE - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kPrecedence_Postfix, "", #OP, callExpr); return + #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Postfix, "", #OP, callExpr); return CASE(PostInc, ++); CASE(PostDec, --); #undef CASE @@ -1603,6 +1796,11 @@ struct EmitVisitor } } } + else if (auto overloadedExpr = funcExpr.As<OverloadedExpr>()) + { + emitUncheckedCallExpr(callExpr, overloadedExpr->lookupResult2.getName(), arg); + return; + } // Fall through to default handling... emitSimpleCallExpr(callExpr, outerPrec); @@ -1612,8 +1810,9 @@ struct EmitVisitor void visitMemberExpressionSyntaxNode(MemberExpressionSyntaxNode* memberExpr, ExprEmitArg const& arg) { + auto prec = kEOp_Postfix; auto outerPrec = arg.outerPrec; - bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Postfix); + bool needClose = MaybeEmitParens(outerPrec, prec); // TODO(tfoley): figure out a good way to reference // declarations that might be generic and/or might @@ -1629,21 +1828,31 @@ struct EmitVisitor } else { - EmitExprWithPrecedence(memberExpr->BaseExpression, kPrecedence_Postfix); + EmitExprWithPrecedence(memberExpr->BaseExpression, leftSide(outerPrec, prec)); Emit("."); } - emitName(memberExpr->declRef.GetName()); + if (!memberExpr->declRef) + { + // This case arises when checking didn't find anything, but we were + // in "rewrite" mode so we blazed ahead anyway. + emitName(memberExpr->name); + } + else + { + emit(memberExpr->declRef.GetName()); + } if(needClose) Emit(")"); } void visitSwizzleExpr(SwizzleExpr* swizExpr, ExprEmitArg const& arg) { + auto prec = kEOp_Postfix; auto outerPrec = arg.outerPrec; - bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Postfix); + bool needClose = MaybeEmitParens(outerPrec, prec); - EmitExprWithPrecedence(swizExpr->base, kPrecedence_Postfix); + EmitExprWithPrecedence(swizExpr->base, leftSide(outerPrec, prec)); Emit("."); static const char* kComponentNames[] = { "x", "y", "z", "w" }; int elementCount = swizExpr->elementCount; @@ -1655,27 +1864,33 @@ struct EmitVisitor if(needClose) Emit(")"); } - void visitIndexExpressionSyntaxNode(IndexExpressionSyntaxNode* indexExpr, ExprEmitArg const& arg) + void visitIndexExpressionSyntaxNode(IndexExpressionSyntaxNode* subscriptExpr, ExprEmitArg const& arg) { + auto prec = kEOp_Postfix; auto outerPrec = arg.outerPrec; - bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Postfix); + bool needClose = MaybeEmitParens(outerPrec, prec); - EmitExprWithPrecedence(indexExpr->BaseExpression, kPrecedence_Postfix); + EmitExprWithPrecedence(subscriptExpr->BaseExpression, leftSide(outerPrec, prec)); Emit("["); - EmitExpr(indexExpr->IndexExpression); + if (auto indexExpr = subscriptExpr->IndexExpression) + { + EmitExpr(indexExpr); + } Emit("]"); if(needClose) Emit(")"); } - void visitOverloadedExpr(OverloadedExpr* expr, ExprEmitArg const& arg) + void visitOverloadedExpr(OverloadedExpr* expr, ExprEmitArg const&) { emitName(expr->lookupResult2.getName()); } void visitVarExpressionSyntaxNode(VarExpressionSyntaxNode* varExpr, ExprEmitArg const& arg) { - bool needClose = MaybeEmitParens(arg.outerPrec, kPrecedence_Atomic); + auto prec = kEOp_Atomic; + auto outerPrec = arg.outerPrec; + bool needClose = MaybeEmitParens(outerPrec, kEOp_Atomic); // TODO: This won't be valid if we had to generate a qualified // reference for some reason. @@ -1696,7 +1911,7 @@ struct EmitVisitor } else { - emitName(varExpr->name); + emit(varExpr->name); } if(needClose) Emit(")"); @@ -1704,16 +1919,14 @@ struct EmitVisitor void visitDerefExpr(DerefExpr* derefExpr, ExprEmitArg const& arg) { - auto outerPrec = arg.outerPrec; - // TODO(tfoley): dereference shouldn't always be implicit - EmitExprWithPrecedence(derefExpr->base, outerPrec); + ExprVisitorWithArg::dispatch(derefExpr->base, arg); } void visitConstantExpressionSyntaxNode(ConstantExpressionSyntaxNode* litExpr, ExprEmitArg const& arg) { auto outerPrec = arg.outerPrec; - bool needClose = MaybeEmitParens(outerPrec, kPrecedence_Atomic); + bool needClose = MaybeEmitParens(outerPrec, kEOp_Atomic); char const* suffix = ""; auto type = litExpr->Type.type; @@ -1775,6 +1988,13 @@ struct EmitVisitor if(needClose) Emit(")"); } + void visitHiddenImplicitCastExpr(HiddenImplicitCastExpr* castExpr, ExprEmitArg const& arg) + { + // This was an implicit cast inserted in code parsed in "rewriter" mode, + // so we don't want to output it and change what the user's code looked like. + ExprVisitorWithArg::dispatch(castExpr->Expression, arg); + } + void visitTypeCastExpressionSyntaxNode(TypeCastExpressionSyntaxNode* castExpr, ExprEmitArg const& arg) { bool needClose = false; @@ -1791,19 +2011,23 @@ struct EmitVisitor default: // HLSL (and C/C++) prefer cast syntax // (In fact, HLSL doesn't allow constructor syntax for some conversions it allows as a cast) - needClose = MaybeEmitParens(arg.outerPrec, kPrecedence_Prefix); - - Emit("("); - EmitType(castExpr->Type); - Emit(")("); - EmitExpr(castExpr->Expression); - Emit(")"); + { + auto prec = kEOp_Prefix; + auto outerPrec = arg.outerPrec; + needClose = MaybeEmitParens(outerPrec, prec); + + Emit("("); + EmitType(castExpr->Type); + Emit(")("); + EmitExpr(castExpr->Expression); + Emit(")"); + } break; } if(needClose) Emit(")"); } - void visitInitializerListExpr(InitializerListExpr* expr, ExprEmitArg const& arg) + void visitInitializerListExpr(InitializerListExpr* expr, ExprEmitArg const&) { Emit("{ "); for(auto& arg : expr->args) @@ -1858,7 +2082,6 @@ struct EmitVisitor } } - void EmitUnparsedStmt(RefPtr<UnparsedStmt> stmt) { // TODO: actually emit the tokens that made up the statement... @@ -1946,12 +2169,10 @@ struct EmitVisitor // The one wrinkle is that HLSL implements the // bad approach to scoping a `for` loop variable, // so we need to avoid those outer `{...}` when - // we are generating HLSL via "rewrite" (that is, - // without our semantic checks). + // we are emitting code that was written in HLSL. // bool brokenScoping = false; - if (context->shared->target == CodeGenTarget::HLSL - && context->isRewrite) + if (forStmt.As<UnscopedForStmt>()) { brokenScoping = true; } @@ -3001,165 +3222,8 @@ struct EmitVisitor throw "unimplemented"; } } - - void registerReservedWord( - String const& name) - { - context->shared->reservedWords.Add(name, name); - } - - void registerReservedWords() - { - #define WORD(NAME) registerReservedWord(#NAME) - - switch (context->shared->target) - { - case CodeGenTarget::GLSL: - WORD(attribute); - WORD(const); - WORD(uniform); - WORD(varying); - WORD(buffer); - - WORD(shared); - WORD(coherent); - WORD(volatile); - WORD(restrict); - WORD(readonly); - WORD(writeonly); - WORD(atomic_unit); - WORD(layout); - WORD(centroid); - WORD(flat); - WORD(smooth); - WORD(noperspective); - WORD(patch); - WORD(sample); - WORD(break); - WORD(continue); - WORD(do); - WORD(for); - WORD(while); - WORD(switch); - WORD(case); - WORD(default); - WORD(if); - WORD(else); - WORD(subroutine); - WORD(in); - WORD(out); - WORD(inout); - WORD(float); - WORD(double); - WORD(int); - WORD(void); - WORD(bool); - WORD(true); - WORD(false); - WORD(invariant); - WORD(precise); - WORD(discard); - WORD(return); - - WORD(lowp); - WORD(mediump); - WORD(highp); - WORD(precision); - WORD(struct); - WORD(uint); - - WORD(common); - WORD(partition); - WORD(active); - WORD(asm); - WORD(class); - WORD(union); - WORD(enum); - WORD(typedef); - WORD(template); - WORD(this); - WORD(resource); - - WORD(goto); - WORD(inline); - WORD(noinline); - WORD(public); - WORD(static); - WORD(extern); - WORD(external); - WORD(interface); - WORD(long); - WORD(short); - WORD(half); - WORD(fixed); - WORD(unsigned); - WORD(superp); - WORD(input); - WORD(output); - WORD(filter); - WORD(sizeof); - WORD(cast); - WORD(namespace); - WORD(using); - - #define CASE(NAME) \ - WORD(NAME ## 2); WORD(NAME ## 3); WORD(NAME ## 4) - - CASE(mat); - CASE(dmat); - CASE(mat2x); - CASE(mat3x); - CASE(mat4x); - CASE(dmat2x); - CASE(dmat3x); - CASE(dmat4x); - CASE(vec); - CASE(ivec); - CASE(bvec); - CASE(dvec); - CASE(uvec); - CASE(hvec); - CASE(fvec); - - #undef CASE - - #define CASE(NAME) \ - WORD(NAME ## 1D); \ - WORD(NAME ## 2D); \ - WORD(NAME ## 3D); \ - WORD(NAME ## Cube); \ - WORD(NAME ## 1DArray); \ - WORD(NAME ## 2DArray); \ - WORD(NAME ## 3DArray); \ - WORD(NAME ## CubeArray);\ - WORD(NAME ## 2DMS); \ - WORD(NAME ## 2DMSArray) \ - /* end */ - - #define CASE2(NAME) \ - CASE(NAME); \ - CASE(i ## NAME); \ - CASE(u ## NAME) \ - /* end */ - - CASE2(sampler); - CASE2(image); - CASE2(texture); - - #undef CASE2 - #undef CASE - break; - - default: - break; - } - } }; -bool isRewriteRequest( - SourceLanguage sourceLanguage, - CodeGenTarget target); - String emitEntryPoint( EntryPointRequest* entryPoint, ProgramLayout* programLayout, @@ -3215,15 +3279,9 @@ String emitEntryPoint( EmitContext context; context.shared = &sharedContext; - context.isRewrite = isRewriteRequest( - translationUnit->sourceLanguage, - target); EmitVisitor visitor(&context); - // TODO: this should only need to take the shared context - visitor.registerReservedWords(); - auto translationUnitSyntax = translationUnit->SyntaxNode.Ptr(); diff --git a/source/slang/expr-defs.h b/source/slang/expr-defs.h index ca5bfacb8..0dac324b9 100644 --- a/source/slang/expr-defs.h +++ b/source/slang/expr-defs.h @@ -91,11 +91,25 @@ SYNTAX_CLASS(DerefExpr, ExpressionSyntaxNode) SYNTAX_FIELD(RefPtr<ExpressionSyntaxNode>, base) END_SYNTAX_CLASS() +// Any operation that performs type-casting SYNTAX_CLASS(TypeCastExpressionSyntaxNode, ExpressionSyntaxNode) SYNTAX_FIELD(TypeExp, TargetType) SYNTAX_FIELD(RefPtr<ExpressionSyntaxNode>, Expression) END_SYNTAX_CLASS() +// An explicit type-cast that appear in the user's code with `(Type) expr` syntax +SYNTAX_CLASS(ExplicitCastExpr, TypeCastExpressionSyntaxNode) +END_SYNTAX_CLASS() + +// An implicit type-cast inserted during semantic checking +SYNTAX_CLASS(ImplicitCastExpr, TypeCastExpressionSyntaxNode) +END_SYNTAX_CLASS() + +// An implicit type-cast that should also be hidden on output, +// because we don't want to mess with the user's code +SYNTAX_CLASS(HiddenImplicitCastExpr, ImplicitCastExpr) +END_SYNTAX_CLASS() + SIMPLE_SYNTAX_CLASS(SelectExpressionSyntaxNode, OperatorExpressionSyntaxNode) SIMPLE_SYNTAX_CLASS(GenericAppExpr, AppExprBase) @@ -112,3 +126,10 @@ SYNTAX_CLASS(AssignExpr, ExpressionSyntaxNode) SYNTAX_FIELD(RefPtr<ExpressionSyntaxNode>, right); END_SYNTAX_CLASS() +// Just an expression inside parentheses `(exp)` +// +// We keep this around explicitly to be sure we don't lose any structure +// when we do rewriter stuff. +SYNTAX_CLASS(ParenExpr, ExpressionSyntaxNode) + SYNTAX_FIELD(RefPtr<ExpressionSyntaxNode>, base); +END_SYNTAX_CLASS() diff --git a/source/slang/intrinsic-defs.h b/source/slang/intrinsic-defs.h index 19a3899a3..a272bd51c 100644 --- a/source/slang/intrinsic-defs.h +++ b/source/slang/intrinsic-defs.h @@ -46,6 +46,7 @@ INTRINSIC(RshAssign) INTRINSIC(OrAssign) INTRINSIC(AndAssign) INTRINSIC(XorAssign) +INTRINSIC(Pos) INTRINSIC(Neg) INTRINSIC(Not) INTRINSIC(BitNot) diff --git a/source/slang/lexer.cpp b/source/slang/lexer.cpp index 2d75e1900..d8211fb20 100644 --- a/source/slang/lexer.cpp +++ b/source/slang/lexer.cpp @@ -517,7 +517,7 @@ namespace Slang if(base > 10) { cursor++; - return c - 'a'; + return 10 + c - 'a'; } return -1; @@ -525,7 +525,7 @@ namespace Slang if(base > 10) { cursor++; - return c - 'A'; + return 10 + c - 'A'; } return -1; } @@ -957,7 +957,7 @@ namespace Slang switch(peek(lexer)) { default: - return TokenType::IntegerLiteral; + return maybeLexNumberSuffix(lexer, TokenType::IntegerLiteral); case '.': advance(lexer); diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp index b90573495..0bb047791 100644 --- a/source/slang/lower.cpp +++ b/source/slang/lower.cpp @@ -194,8 +194,17 @@ public: struct SharedLoweringContext { ProgramLayout* programLayout; + + // The target we are going to generate code for. + // + // We may need to specialize how constructs get lowered based + // on the capabilities of the target language. CodeGenTarget target; + // A set of words reserved by the target + Dictionary<String, String> reservedWords; + + RefPtr<ProgramSyntaxNode> loweredProgram; Dictionary<Decl*, RefPtr<Decl>> loweredDecls; @@ -238,6 +247,165 @@ struct LoweringVisitor CodeGenTarget getTarget() { return shared->target; } + bool isReservedWord(String const& name) + { + return shared->reservedWords.TryGetValue(name) != nullptr; + } + + void registerReservedWord( + String const& name) + { + shared->reservedWords.Add(name, name); + } + + void registerReservedWords() + { + #define WORD(NAME) registerReservedWord(#NAME) + + switch (shared->target) + { + case CodeGenTarget::GLSL: + WORD(attribute); + WORD(const); + WORD(uniform); + WORD(varying); + WORD(buffer); + + WORD(shared); + WORD(coherent); + WORD(volatile); + WORD(restrict); + WORD(readonly); + WORD(writeonly); + WORD(atomic_unit); + WORD(layout); + WORD(centroid); + WORD(flat); + WORD(smooth); + WORD(noperspective); + WORD(patch); + WORD(sample); + WORD(break); + WORD(continue); + WORD(do); + WORD(for); + WORD(while); + WORD(switch); + WORD(case); + WORD(default); + WORD(if); + WORD(else); + WORD(subroutine); + WORD(in); + WORD(out); + WORD(inout); + WORD(float); + WORD(double); + WORD(int); + WORD(void); + WORD(bool); + WORD(true); + WORD(false); + WORD(invariant); + WORD(precise); + WORD(discard); + WORD(return); + + WORD(lowp); + WORD(mediump); + WORD(highp); + WORD(precision); + WORD(struct); + WORD(uint); + + WORD(common); + WORD(partition); + WORD(active); + WORD(asm); + WORD(class); + WORD(union); + WORD(enum); + WORD(typedef); + WORD(template); + WORD(this); + WORD(resource); + + WORD(goto); + WORD(inline); + WORD(noinline); + WORD(public); + WORD(static); + WORD(extern); + WORD(external); + WORD(interface); + WORD(long); + WORD(short); + WORD(half); + WORD(fixed); + WORD(unsigned); + WORD(superp); + WORD(input); + WORD(output); + WORD(filter); + WORD(sizeof); + WORD(cast); + WORD(namespace); + WORD(using); + + #define CASE(NAME) \ + WORD(NAME ## 2); WORD(NAME ## 3); WORD(NAME ## 4) + + CASE(mat); + CASE(dmat); + CASE(mat2x); + CASE(mat3x); + CASE(mat4x); + CASE(dmat2x); + CASE(dmat3x); + CASE(dmat4x); + CASE(vec); + CASE(ivec); + CASE(bvec); + CASE(dvec); + CASE(uvec); + CASE(hvec); + CASE(fvec); + + #undef CASE + + #define CASE(NAME) \ + WORD(NAME ## 1D); \ + WORD(NAME ## 2D); \ + WORD(NAME ## 3D); \ + WORD(NAME ## Cube); \ + WORD(NAME ## 1DArray); \ + WORD(NAME ## 2DArray); \ + WORD(NAME ## 3DArray); \ + WORD(NAME ## CubeArray);\ + WORD(NAME ## 2DMS); \ + WORD(NAME ## 2DMSArray) \ + /* end */ + + #define CASE2(NAME) \ + CASE(NAME); \ + CASE(i ## NAME); \ + CASE(u ## NAME) \ + /* end */ + + CASE2(sampler); + CASE2(image); + CASE2(texture); + + #undef CASE2 + #undef CASE + break; + + default: + break; + } + } + + // // Values // @@ -451,6 +619,7 @@ struct LoweringVisitor lowerExprCommon(loweredExpr, expr); loweredExpr->BaseExpression = loweredBase; loweredExpr->declRef = loweredDeclRef; + loweredExpr->name = expr->name; return loweredExpr; } @@ -732,10 +901,10 @@ struct LoweringVisitor addStmt(loweredStmt); } - - void visitForStatementSyntaxNode(ForStatementSyntaxNode* stmt) + void lowerForStmtCommon( + RefPtr<ForStatementSyntaxNode> loweredStmt, + ForStatementSyntaxNode* stmt) { - RefPtr<ForStatementSyntaxNode> loweredStmt = new ForStatementSyntaxNode(); lowerScopeStmtFields(loweredStmt, stmt); LoweringVisitor subVisitor = pushScope(loweredStmt, stmt); @@ -748,6 +917,16 @@ struct LoweringVisitor addStmt(loweredStmt); } + void visitForStatementSyntaxNode(ForStatementSyntaxNode* stmt) + { + lowerForStmtCommon(new ForStatementSyntaxNode(), stmt); + } + + void visitUnscopedForStmt(UnscopedForStmt* stmt) + { + lowerForStmtCommon(new UnscopedForStmt(), stmt); + } + void visitWhileStatementSyntaxNode(WhileStatementSyntaxNode* stmt) { RefPtr<WhileStatementSyntaxNode> loweredStmt = new WhileStatementSyntaxNode(); @@ -964,6 +1143,43 @@ struct LoweringVisitor shared->mapLoweredDeclToOriginal.Add(loweredDecl, decl); } + // If the name of the declarations collides with a reserved word + // for the code generation target, then rename it to avoid the conflict + // + // Note that this does *not* implement any kind of comprehensive renaming + // to, e.g., avoid conflicts between user-defined and library functions. + void ensureDeclHasAValidName(Decl* decl) + { + // By default, we would like to emit a name in the generated + // code exactly as it appeared in the original program. + // When that isn't possible, we'd like to emit a name as + // close to the original as possible (to ensure that existing + // debugging tools still work reasonably well). + // + // One reason why a name might not be allowed as-is is that + // it could collide with a reserved word in the target language. + // Another reason is that it might not follow a naming convention + // imposed by the target (e.g., in GLSL names starting with + // `gl_` or containing `__` are reserved). + // + // Given a name that should not be allowed, we want to + // change it to a name that *is* allowed. e.g., by adding + // `_` to the end of a reserved word. + // + // The next problem this creates is that the modified name + // could not collide with an existing use of the same + // (valid) name. + // + // For now we are going to solve this problem in a simple + // and ad hoc fashion, but longer term we'll want to do + // something sytematic. + + if (isReservedWord(decl->getName())) + { + decl->Name.Content.append("_"); + } + } + void lowerDeclCommon( Decl* loweredDecl, Decl* decl) @@ -973,6 +1189,9 @@ struct LoweringVisitor loweredDecl->Position = decl->Position; loweredDecl->Name = decl->getNameToken(); + // Deal with renaming - we shouldn't allow decls with names that are reserved words + ensureDeclHasAValidName(loweredDecl); + // Lower modifiers as needed // HACK: just doing a shallow copy of modifiers, which will @@ -1314,6 +1533,8 @@ struct LoweringVisitor globalVarDecl->Name.Content = info.name; globalVarDecl->Type.type = type; + ensureDeclHasAValidName(globalVarDecl); + addMember(shared->loweredProgram, globalVarDecl); // Add the layout information @@ -1515,6 +1736,8 @@ struct LoweringVisitor localVarDecl->Name.Content = paramDecl->getName(); localVarDecl->Type = lowerType(paramDecl->Type); + ensureDeclHasAValidName(localVarDecl); + subVisitor.addDecl(localVarDecl); EntryPointParamPair paramPair; @@ -1549,6 +1772,8 @@ struct LoweringVisitor resultVarDecl->Name.Content = "_main_result"; resultVarDecl->Type = TypeExp(loweredEntryPointFunc->ReturnType); + ensureDeclHasAValidName(resultVarDecl); + subVisitor.addDecl(resultVarDecl); } @@ -1793,6 +2018,9 @@ LoweredEntryPoint lowerEntryPoint( visitor.shared = &sharedContext; visitor.parentDecl = loweredProgram; + // TODO: this should only need to take the shared context + visitor.registerReservedWords(); + // We need to register the lowered program as the lowered version // of the existing translation unit declaration. diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index b134f9645..759fc0c20 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -44,6 +44,11 @@ namespace Slang String fileName; int genericDepth = 0; + // Have we seen any `import` declarations? If so, we need + // to parse function bodies completely, even if we are in + // "rewrite" mode. + bool haveSeenAnyImportDecls = false; + // Is the parser in a "recovering" state? // During recovery we don't emit additional errors, until we find // a token that we expected, when we exit recovery. @@ -802,6 +807,8 @@ namespace Slang static RefPtr<Decl> parseImportDecl( Parser* parser) { + parser->haveSeenAnyImportDecls = true; + parser->ReadToken("__import"); auto decl = new ImportDecl(); @@ -842,6 +849,8 @@ namespace Slang static RefPtr<Decl> parsePoundImportDecl( Parser* parser) { + parser->haveSeenAnyImportDecls = true; + Token importToken = parser->ReadToken(TokenType::PoundImport); auto decl = new ImportDecl(); @@ -1392,6 +1401,28 @@ namespace Slang return genericApp; } + // Parse option `[]` braces after a type expression, that indicate an array type + static RefPtr<ExpressionSyntaxNode> parsePostfixTypeSuffix( + Parser* parser, + RefPtr<ExpressionSyntaxNode> inTypeExpr) + { + auto typeExpr = inTypeExpr; + while (parser->LookAheadToken(TokenType::LBracket)) + { + RefPtr<IndexExpressionSyntaxNode> arrType = new IndexExpressionSyntaxNode(); + arrType->Position = typeExpr->Position; + arrType->BaseExpression = typeExpr; + parser->ReadToken(TokenType::LBracket); + if (!parser->LookAheadToken(TokenType::RBracket)) + { + arrType->IndexExpression = parser->ParseExpression(); + } + parser->ReadToken(TokenType::RBracket); + typeExpr = arrType; + } + return typeExpr; + } + static TypeSpec parseTypeSpec(Parser* parser) { @@ -1431,11 +1462,16 @@ namespace Slang typeExpr = parseGenericApp(parser, typeExpr); } + // GLSL allows `[]` directly in a type specifier + if (parser->translationUnit->sourceLanguage == SourceLanguage::GLSL) + { + typeExpr = parsePostfixTypeSuffix(parser, typeExpr); + } + typeSpec.expr = typeExpr; return typeSpec; } - static RefPtr<DeclBase> ParseDeclaratorDecl( Parser* parser, ContainerDecl* containerDecl) @@ -2572,7 +2608,11 @@ namespace Slang RefPtr<StatementSyntaxNode> Parser::ParseBlockStatement() { - if( translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING ) + // If we are being asked not to check things *and* we haven't + // seen any `import` declarations yet, then we can safely assume + // that function bodies should be left as-is. + if( (translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING) + && !haveSeenAnyImportDecls ) { // We have been asked to parse the input, but not attempt to understand it. @@ -2615,7 +2655,6 @@ namespace Slang return unparsedStmt; } - RefPtr<ScopeDecl> scopeDecl = new ScopeDecl(); RefPtr<BlockStmt> blockStatement = new BlockStmt(); blockStatement->scopeDecl = scopeDecl; @@ -2690,12 +2729,32 @@ namespace Slang RefPtr<ForStatementSyntaxNode> Parser::ParseForStatement() { RefPtr<ScopeDecl> scopeDecl = new ScopeDecl(); - RefPtr<ForStatementSyntaxNode> stmt = new ForStatementSyntaxNode(); + + // HLSL implements the bad approach to scoping a `for` loop + // variable, and we want to respect that, but *only* when + // parsing HLSL code. + // + + bool brokenScoping = translationUnit->sourceLanguage == SourceLanguage::HLSL; + + // We will create a distinct syntax node class for the unscoped + // case, just so that we can correctly handle it in downstream + // logic. + // + RefPtr<ForStatementSyntaxNode> stmt; + if (brokenScoping) + { + stmt = new UnscopedForStmt(); + } + else + { + stmt = new ForStatementSyntaxNode(); + } + stmt->scopeDecl = scopeDecl; - // Note(tfoley): HLSL implements `for` with incorrect scoping. - // We need an option to turn on this behavior in a kind of "legacy" mode -// PushScope(scopeDecl.Ptr()); + if(!brokenScoping) + PushScope(scopeDecl.Ptr()); FillPosition(stmt.Ptr()); ReadToken("for"); ReadToken(TokenType::LParent); @@ -2721,7 +2780,10 @@ namespace Slang stmt->SideEffectExpression = ParseExpression(); ReadToken(TokenType::RParent); stmt->Statement = ParseStatement(); -// PopScope(); + + if (!brokenScoping) + PopScope(); + return stmt; } @@ -2816,19 +2878,7 @@ namespace Slang } auto typeExpr = typeSpec.expr; - while (LookAheadToken(TokenType::LBracket)) - { - RefPtr<IndexExpressionSyntaxNode> arrType = new IndexExpressionSyntaxNode(); - arrType->Position = typeExpr->Position; - arrType->BaseExpression = typeExpr; - ReadToken(TokenType::LBracket); - if (!LookAheadToken(TokenType::RBracket)) - { - arrType->IndexExpression = ParseExpression(); - } - ReadToken(TokenType::RBracket); - typeExpr = arrType; - } + typeExpr = parsePostfixTypeSuffix(this, typeExpr); return typeExpr; } @@ -2915,83 +2965,6 @@ namespace Slang } } - Operator GetOpFromToken(Token & token) - { - switch(token.Type) - { - case TokenType::Comma: - return Operator::Sequence; - case TokenType::OpAssign: - return Operator::Assign; - case TokenType::OpAddAssign: - return Operator::AddAssign; - case TokenType::OpSubAssign: - return Operator::SubAssign; - case TokenType::OpMulAssign: - return Operator::MulAssign; - case TokenType::OpDivAssign: - return Operator::DivAssign; - case TokenType::OpModAssign: - return Operator::ModAssign; - case TokenType::OpShlAssign: - return Operator::LshAssign; - case TokenType::OpShrAssign: - return Operator::RshAssign; - case TokenType::OpOrAssign: - return Operator::OrAssign; - case TokenType::OpAndAssign: - return Operator::AddAssign; - case TokenType::OpXorAssign: - return Operator::XorAssign; - case TokenType::OpOr: - return Operator::Or; - case TokenType::OpAnd: - return Operator::And; - case TokenType::OpBitOr: - return Operator::BitOr; - case TokenType::OpBitXor: - return Operator::BitXor; - case TokenType::OpBitAnd: - return Operator::BitAnd; - case TokenType::OpEql: - return Operator::Eql; - case TokenType::OpNeq: - return Operator::Neq; - case TokenType::OpGeq: - return Operator::Geq; - case TokenType::OpLeq: - return Operator::Leq; - case TokenType::OpGreater: - return Operator::Greater; - case TokenType::OpLess: - return Operator::Less; - case TokenType::OpLsh: - return Operator::Lsh; - case TokenType::OpRsh: - return Operator::Rsh; - case TokenType::OpAdd: - return Operator::Add; - case TokenType::OpSub: - return Operator::Sub; - case TokenType::OpMul: - return Operator::Mul; - case TokenType::OpDiv: - return Operator::Div; - case TokenType::OpMod: - return Operator::Mod; - case TokenType::OpInc: - return Operator::PostInc; - case TokenType::OpDec: - return Operator::PostDec; - case TokenType::OpNot: - return Operator::Not; - case TokenType::OpBitNot: - return Operator::BitNot; - default: - throw "Illegal TokenType."; - } - } - static RefPtr<ExpressionSyntaxNode> parseOperator(Parser* parser) { Token opToken; @@ -3194,25 +3167,27 @@ namespace Slang // but for now we will follow some hueristics. case TokenType::LParent: { - parser->ReadToken(TokenType::LParent); + Token openParen = parser->ReadToken(TokenType::LParent); - RefPtr<ExpressionSyntaxNode> expr; if (peekTypeName(parser) && parser->LookAheadToken(TokenType::RParent, 1)) { - RefPtr<TypeCastExpressionSyntaxNode> tcexpr = new TypeCastExpressionSyntaxNode(); + RefPtr<TypeCastExpressionSyntaxNode> tcexpr = new ExplicitCastExpr(); 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; + return tcexpr; } else { - expr = parser->ParseExpression(); + RefPtr<ExpressionSyntaxNode> base = parser->ParseExpression(); parser->ReadToken(TokenType::RParent); - } - return expr; + RefPtr<ParenExpr> parenExpr = new ParenExpr(); + parenExpr->Position = openParen.Position; + parenExpr->base = base; + return parenExpr; + } } // An initializer list `{ expr, ... }` @@ -3476,7 +3451,11 @@ namespace Slang indexExpr->BaseExpression = expr; parser->FillPosition(indexExpr.Ptr()); parser->ReadToken(TokenType::LBracket); - indexExpr->IndexExpression = parser->ParseExpression(); + // TODO: eventually we may want to support multiple arguments inside the `[]` + if (!parser->LookAheadToken(TokenType::RBracket)) + { + indexExpr->IndexExpression = parser->ParseExpression(); + } parser->ReadToken(TokenType::RBracket); expr = indexExpr; @@ -3539,6 +3518,7 @@ namespace Slang case TokenType::OpDec: case TokenType::OpNot: case TokenType::OpBitNot: + case TokenType::OpAdd: case TokenType::OpSub: { RefPtr<PrefixExpr> prefixExpr = new PrefixExpr(); diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp index e1bba8885..e2d5829b4 100644 --- a/source/slang/slang-stdlib.cpp +++ b/source/slang/slang-stdlib.cpp @@ -7,18 +7,40 @@ #define STRINGIZE2(x) #x #define LINE_STRING STRINGIZE(__LINE__) -enum { kLibIncludeStringLine = __LINE__+1 }; -const char * LibIncludeStringChunks[] = { R"=( +enum { kCoreLibIncludeStringLine = __LINE__ + 1 }; +const char* kCoreLibIncludeStringChunks[] = { R"=( -typedef uint UINT; +// A type that can be used as an operand for builtins +interface __BuiltinType {} + +// A type that can be used for arithmetic operations +interface __BuiltinArithmeticType : __BuiltinType {} + +// A type that logically has a sign (positive/negative/zero) +interface __BuiltinSignedArithmeticType : __BuiltinArithmeticType {} -__generic<T> __intrinsic_op(Assign) T operator=(out T left, T right); +// A type that can represent integers +interface __BuiltinIntegerType : __BuiltinArithmeticType {} + +// A type that can represent non-integers +interface __BuiltinRealType : __BuiltinArithmeticType {} + +// A type that uses a floating-point representation +interface __BuiltinFloatingPointType : __BuiltinRealType, __BuiltinSignedArithmeticType {} __generic<T,U> __intrinsic_op(Sequence) U operator,(T left, U right); __generic<T> __intrinsic_op(Select) T operator?:(bool condition, T ifTrue, T ifFalse); __generic<T, let N : int> __intrinsic_op(Select) vector<T,N> operator?:(vector<bool,N> condition, vector<T,N> ifTrue, vector<T,N> ifFalse); +)=" }; + + +enum { kHLSLLibIncludeStringLine = __LINE__+1 }; +const char * kHLSLLibIncludeStringChunks[] = { R"=( + +typedef uint UINT; + __generic<T> __magic_type(HLSLAppendStructuredBufferType) struct AppendStructuredBuffer { __intrinsic void Append(T value); @@ -252,24 +274,6 @@ __generic<T> __magic_type(HLSLLineStreamType) struct TriangleStream // Note(tfoley): Trying to systematically add all the HLSL builtins -// A type that can be used as an operand for builtins -interface __BuiltinType {} - -// A type that can be used for arithmetic operations -interface __BuiltinArithmeticType : __BuiltinType {} - -// A type that logically has a sign (positive/negative/zero) -interface __BuiltinSignedArithmeticType : __BuiltinArithmeticType {} - -// A type that can represent integers -interface __BuiltinIntegerType : __BuiltinArithmeticType {} - -// A type that can represent non-integers -interface __BuiltinRealType : __BuiltinArithmeticType {} - -// A type that uses a floating-point representation -interface __BuiltinFloatingPointType : __BuiltinRealType, __BuiltinSignedArithmeticType {} - // Try to terminate the current draw or dispatch call (HLSL SM 4.0) __intrinsic void abort(); @@ -1011,7 +1015,11 @@ namespace Slang return stdlibPath; } - String SlangStdLib::code; + // Cached code for the various libraries + String coreLibraryCode; + String slangLibraryCode; + String hlslLibraryCode; + String glslLibraryCode; enum { @@ -1029,93 +1037,88 @@ namespace Slang ANY_MASK = INT_MASK | FLOAT_MASK | BOOL_MASK, }; - String SlangStdLib::GetCode() - { - if (code.Length() > 0) - return code; - StringBuilder sb; - - // generate operator overloads - - - - struct OpInfo { IntrinsicOp opCode; char const* opName; unsigned flags; }; - - OpInfo unaryOps[] = { - { IntrinsicOp::Neg, "-", ARITHMETIC_MASK }, - { IntrinsicOp::Not, "!", ANY_MASK }, - { IntrinsicOp::BitNot, "~", INT_MASK }, - { IntrinsicOp::PreInc, "++", ARITHMETIC_MASK | ASSIGNMENT }, - { IntrinsicOp::PreDec, "--", ARITHMETIC_MASK | ASSIGNMENT }, - { IntrinsicOp::PostInc, "++", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX }, - { IntrinsicOp::PostDec, "--", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX }, - }; - - OpInfo binaryOps[] = { - { IntrinsicOp::Add, "+", ARITHMETIC_MASK }, - { IntrinsicOp::Sub, "-", ARITHMETIC_MASK }, - { IntrinsicOp::Mul, "*", ARITHMETIC_MASK }, - { IntrinsicOp::Div, "/", ARITHMETIC_MASK }, - { IntrinsicOp::Mod, "%", INT_MASK }, - - { IntrinsicOp::And, "&&", LOGICAL_MASK }, - { IntrinsicOp::Or, "||", LOGICAL_MASK }, - - { IntrinsicOp::BitAnd, "&", LOGICAL_MASK }, - { IntrinsicOp::BitOr, "|", LOGICAL_MASK }, - { IntrinsicOp::BitXor, "^", LOGICAL_MASK }, + static const struct { + char const* name; + BaseType tag; + unsigned flags; + } kBaseTypes[] = { + { "void", BaseType::Void, 0 }, + { "int", BaseType::Int, SINT_MASK }, + { "half", BaseType::Half, FLOAT_MASK }, + { "float", BaseType::Float, FLOAT_MASK }, + { "double", BaseType::Double, FLOAT_MASK }, + { "uint", BaseType::UInt, UINT_MASK }, + { "bool", BaseType::Bool, BOOL_MASK }, + { "uint64_t", BaseType::UInt64, UINT_MASK }, + }; - { IntrinsicOp::Lsh, "<<", INT_MASK }, - { IntrinsicOp::Rsh, ">>", INT_MASK }, + struct OpInfo { IntrinsicOp opCode; char const* opName; unsigned flags; }; + + static const OpInfo unaryOps[] = { + { IntrinsicOp::Pos, "+", ARITHMETIC_MASK }, + { IntrinsicOp::Neg, "-", ARITHMETIC_MASK }, + { IntrinsicOp::Not, "!", ANY_MASK }, + { IntrinsicOp::BitNot, "~", INT_MASK }, + { IntrinsicOp::PreInc, "++", ARITHMETIC_MASK | ASSIGNMENT }, + { IntrinsicOp::PreDec, "--", ARITHMETIC_MASK | ASSIGNMENT }, + { IntrinsicOp::PostInc, "++", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX }, + { IntrinsicOp::PostDec, "--", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX }, + }; - { IntrinsicOp::Eql, "==", ANY_MASK | COMPARISON }, - { IntrinsicOp::Neq, "!=", ANY_MASK | COMPARISON }, + static const OpInfo binaryOps[] = { + { IntrinsicOp::Add, "+", ARITHMETIC_MASK }, + { IntrinsicOp::Sub, "-", ARITHMETIC_MASK }, + { IntrinsicOp::Mul, "*", ARITHMETIC_MASK }, + { IntrinsicOp::Div, "/", ARITHMETIC_MASK }, + { IntrinsicOp::Mod, "%", INT_MASK }, + + { IntrinsicOp::And, "&&", LOGICAL_MASK }, + { IntrinsicOp::Or, "||", LOGICAL_MASK }, + + { IntrinsicOp::BitAnd, "&", LOGICAL_MASK }, + { IntrinsicOp::BitOr, "|", LOGICAL_MASK }, + { IntrinsicOp::BitXor, "^", LOGICAL_MASK }, + + { IntrinsicOp::Lsh, "<<", INT_MASK }, + { IntrinsicOp::Rsh, ">>", INT_MASK }, + + { IntrinsicOp::Eql, "==", ANY_MASK | COMPARISON }, + { IntrinsicOp::Neq, "!=", ANY_MASK | COMPARISON }, + + { IntrinsicOp::Greater, ">", ARITHMETIC_MASK | COMPARISON }, + { IntrinsicOp::Less, "<", ARITHMETIC_MASK | COMPARISON }, + { IntrinsicOp::Geq, ">=", ARITHMETIC_MASK | COMPARISON }, + { IntrinsicOp::Leq, "<=", ARITHMETIC_MASK | COMPARISON }, + + { IntrinsicOp::AddAssign, "+=", ASSIGNMENT | ARITHMETIC_MASK }, + { IntrinsicOp::SubAssign, "-=", ASSIGNMENT | ARITHMETIC_MASK }, + { IntrinsicOp::MulAssign, "*=", ASSIGNMENT | ARITHMETIC_MASK }, + { IntrinsicOp::DivAssign, "/=", ASSIGNMENT | ARITHMETIC_MASK }, + { IntrinsicOp::ModAssign, "%=", ASSIGNMENT | ARITHMETIC_MASK }, + { IntrinsicOp::AndAssign, "&=", ASSIGNMENT | LOGICAL_MASK }, + { IntrinsicOp::OrAssign, "|=", ASSIGNMENT | LOGICAL_MASK }, + { IntrinsicOp::XorAssign, "^=", ASSIGNMENT | LOGICAL_MASK }, + { IntrinsicOp::LshAssign, "<<=", ASSIGNMENT | INT_MASK }, + { IntrinsicOp::RshAssign, ">>=", ASSIGNMENT | INT_MASK }, + }; - { IntrinsicOp::Greater, ">", ARITHMETIC_MASK | COMPARISON }, - { IntrinsicOp::Less, "<", ARITHMETIC_MASK | COMPARISON }, - { IntrinsicOp::Geq, ">=", ARITHMETIC_MASK | COMPARISON }, - { IntrinsicOp::Leq, "<=", ARITHMETIC_MASK | COMPARISON }, - { IntrinsicOp::AddAssign, "+=", ASSIGNMENT | ARITHMETIC_MASK }, - { IntrinsicOp::SubAssign, "-=", ASSIGNMENT | ARITHMETIC_MASK }, - { IntrinsicOp::MulAssign, "*=", ASSIGNMENT | ARITHMETIC_MASK }, - { IntrinsicOp::DivAssign, "/=", ASSIGNMENT | ARITHMETIC_MASK }, - { IntrinsicOp::ModAssign, "%=", ASSIGNMENT | ARITHMETIC_MASK }, - { IntrinsicOp::AndAssign, "&=", ASSIGNMENT | LOGICAL_MASK }, - { IntrinsicOp::OrAssign, "|=", ASSIGNMENT | LOGICAL_MASK }, - { IntrinsicOp::XorAssign, "^=", ASSIGNMENT | LOGICAL_MASK }, - { IntrinsicOp::LshAssign, "<<=", ASSIGNMENT | INT_MASK }, - { IntrinsicOp::RshAssign, ">>=", ASSIGNMENT | INT_MASK }, + String getCoreLibraryCode() + { + if (coreLibraryCode.Length() > 0) + return coreLibraryCode; + StringBuilder sb; - }; + // generate operator overloads - /* - String floatTypes[] = { "float", "float2", "float3", "float4" }; - String intTypes[] = { "int", "int2", "int3", "int4" }; - String uintTypes[] = { "uint", "uint2", "uint3", "uint4" }; - */ String path = getStdlibPath(); - - #define EMIT_LINE_DIRECTIVE() sb << "#line " << (__LINE__+1) << " \"" << path << "\"\n" // Generate declarations for all the base types - static const struct { - char const* name; - BaseType tag; - unsigned flags; - } kBaseTypes[] = { - { "void", BaseType::Void, 0 }, - { "int", BaseType::Int, SINT_MASK }, - { "float", BaseType::Float, FLOAT_MASK }, - { "uint", BaseType::UInt, UINT_MASK }, - { "bool", BaseType::Bool, BOOL_MASK }, - { "uint64_t", BaseType::UInt64, UINT_MASK }, - }; static const int kBaseTypeCount = sizeof(kBaseTypes) / sizeof(kBaseTypes[0]); for (int tt = 0; tt < kBaseTypeCount; ++tt) { @@ -1126,7 +1129,7 @@ namespace Slang sb << "\n : __BuiltinType\n"; - switch( kBaseTypes[tt].tag ) + switch (kBaseTypes[tt].tag) { case BaseType::Float: sb << "\n , __BuiltinFloatingPointType\n"; @@ -1151,9 +1154,9 @@ namespace Slang // Declare initializers to convert from various other types - for( int ss = 0; ss < kBaseTypeCount; ++ss ) + for (int ss = 0; ss < kBaseTypeCount; ++ss) { - if( kBaseTypes[ss].tag == BaseType::Void ) + if (kBaseTypes[ss].tag == BaseType::Void) continue; EMIT_LINE_DIRECTIVE(); @@ -1163,12 +1166,6 @@ namespace Slang sb << "};\n"; } - // Declare ad hoc aliases for some types, just to get things compiling - // - // TODO(tfoley): At the very least, `double` should be treated as a distinct type. - sb << "typedef float double;\n"; - sb << "typedef float half;\n"; - // Declare vector and matrix types sb << "__generic<T = float, let N : int = 4> __magic_type(Vector) struct vector\n{\n"; @@ -1205,6 +1202,12 @@ namespace Slang } } + // Declare additional built-in generic types +// EMIT_LINE_DIRECTIVE(); + sb << "__generic<T> __magic_type(ConstantBuffer) struct ConstantBuffer {};\n"; + sb << "__generic<T> __magic_type(TextureBuffer) struct TextureBuffer {};\n"; + + static const char* kComponentNames[]{ "x", "y", "z", "w" }; static const char* kVectorNames[]{ "", "x", "xy", "xyz", "xyzw" }; @@ -1292,7 +1295,6 @@ namespace Slang sb << "}\n"; } - // Declare built-in texture and sampler types sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerState) << ") struct SamplerState {};"; @@ -1372,11 +1374,11 @@ namespace Slang for(int isFloat = 0; isFloat < 2; ++isFloat) for(int includeMipInfo = 0; includeMipInfo < 2; ++includeMipInfo) { - char const* t = isFloat ? "out float " : "out UINT "; + char const* t = isFloat ? "out float " : "out uint "; sb << "void GetDimensions("; if(includeMipInfo) - sb << "UINT mipLevel, "; + sb << "uint mipLevel, "; switch(baseShape) { @@ -1671,27 +1673,6 @@ namespace Slang } } - // Declare additional built-in generic types - EMIT_LINE_DIRECTIVE(); - sb << "__generic<T> __magic_type(ConstantBuffer) struct ConstantBuffer {};\n"; - sb << "__generic<T> __magic_type(TextureBuffer) struct TextureBuffer {};\n"; - - sb << "__generic<T> __magic_type(PackedBuffer) struct PackedBuffer {};\n"; - sb << "__generic<T> __magic_type(Uniform) struct Uniform {};\n"; - sb << "__generic<T> __magic_type(Patch) struct Patch {};\n"; - - - // Stale declarations for GLSL inner-product builtins -#if 0 - sb << "__intrinsic vec3 operator * (vec3, mat3);\n"; - sb << "__intrinsic vec3 operator * (mat3, vec3);\n"; - - sb << "__intrinsic vec4 operator * (vec4, mat4);\n"; - sb << "__intrinsic vec4 operator * (mat4, vec4);\n"; - - sb << "__intrinsic mat3 operator * (mat3, mat3);\n"; - sb << "__intrinsic mat4 operator * (mat4, mat4);\n"; -#endif for (auto op : unaryOps) { @@ -1745,29 +1726,93 @@ namespace Slang sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << "vector<" << leftType << ",N> left, vector<" << rightType << ",N> right);\n"; // matrix version + + // skip matrix-matrix multiply operations here, so that GLSL doesn't see them + switch (op.opCode) + { + case IntrinsicOp::Mul: + case IntrinsicOp::MulAssign: + break; + + default: + sb << "__generic<let N : int, let M : int> "; + sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, matrix<" << rightType << ",N,M> right);\n"; + break; + } + } + } + + // Output a suitable `#line` directive to point at our raw stdlib code above + sb << "\n#line " << kCoreLibIncludeStringLine << " \"" << path << "\"\n"; + + int chunkCount = sizeof(kCoreLibIncludeStringChunks) / sizeof(kCoreLibIncludeStringChunks[0]); + for (int cc = 0; cc < chunkCount; ++cc) + { + sb << kCoreLibIncludeStringChunks[cc]; + } + + coreLibraryCode = sb.ProduceString(); + return coreLibraryCode; + } + + String getHLSLLibraryCode() + { + if (hlslLibraryCode.Length() > 0) + return hlslLibraryCode; + + StringBuilder sb; + + +// sb << "__generic<T> __magic_type(PackedBuffer) struct PackedBuffer {};\n"; +// sb << "__generic<T> __magic_type(Uniform) struct Uniform {};\n"; +// sb << "__generic<T> __magic_type(Patch) struct Patch {};\n"; + + // Component-wise multiplication ops + for(auto op : binaryOps) + { + switch (op.opCode) + { + default: + continue; + + case IntrinsicOp::Mul: + case IntrinsicOp::MulAssign: + break; + } + + for (auto type : kBaseTypes) + { + if ((type.flags & op.flags) == 0) + continue; + + char const* leftType = type.name; + char const* rightType = leftType; + char const* resultType = leftType; + + char const* leftQual = ""; + if(op.flags & ASSIGNMENT) leftQual = "in out "; + sb << "__generic<let N : int, let M : int> "; sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, matrix<" << rightType << ",N,M> right);\n"; } } // Output a suitable `#line` directive to point at our raw stdlib code above - sb << "\n#line " << kLibIncludeStringLine << " \"" << path << "\"\n"; + sb << "\n#line " << kHLSLLibIncludeStringLine << " \"" << getStdlibPath() << "\"\n"; - int chunkCount = sizeof(LibIncludeStringChunks) / sizeof(LibIncludeStringChunks[0]); + int chunkCount = sizeof(kHLSLLibIncludeStringChunks) / sizeof(kHLSLLibIncludeStringChunks[0]); for (int cc = 0; cc < chunkCount; ++cc) { - sb << LibIncludeStringChunks[cc]; + sb << kHLSLLibIncludeStringChunks[cc]; } - code = sb.ProduceString(); - return code; + hlslLibraryCode = sb.ProduceString(); + return hlslLibraryCode; } // GLSL-specific library code - String glslLibraryCode; - String getGLSLLibraryCode() { if(glslLibraryCode.Length() != 0) @@ -1794,16 +1839,42 @@ namespace Slang // Declare GLSL aliases for HLSL types for (int vv = 2; vv <= 4; ++vv) { - sb << "typedef " << kTypes[tt].name << vv << " " << kTypes[tt].glslPrefix << "vec" << vv << ";\n"; - sb << "typedef " << kTypes[tt].name << vv << "x" << vv << " " << kTypes[tt].glslPrefix << "mat" << vv << ";\n"; + sb << "typedef vector<" << kTypes[tt].name << "," << vv << "> " << kTypes[tt].glslPrefix << "vec" << vv << ";\n"; + sb << "typedef matrix<" << kTypes[tt].name << "," << vv << "," << vv << "> " << kTypes[tt].glslPrefix << "mat" << vv << ";\n"; } for (int rr = 2; rr <= 4; ++rr) for (int cc = 2; cc <= 4; ++cc) { - sb << "typedef " << kTypes[tt].name << rr << "x" << cc << " " << kTypes[tt].glslPrefix << "mat" << rr << "x" << cc << ";\n"; + sb << "typedef matrix<" << kTypes[tt].name << "," << rr << "," << cc << "> " << kTypes[tt].glslPrefix << "mat" << rr << "x" << cc << ";\n"; } } + // Multiplication operations for vectors + matrices + + // scalar-vector and vector-scalar + sb << "__generic<T : __BuiltinArithmeticType, let N : int> __intrinsic_op(Mul) vector<T,N> operator*(vector<T,N> x, T y);\n"; + sb << "__generic<T : __BuiltinArithmeticType, let N : int> __intrinsic_op(Mul) vector<T,N> operator*(T x, vector<T,N> y);\n"; + + // scalar-matrix and matrix-scalar + sb << "__generic<T : __BuiltinArithmeticType, let N : int, let M :int> __intrinsic_op(Mul) matrix<T,N,M> operator*(matrix<T,N,M> x, T y);\n"; + sb << "__generic<T : __BuiltinArithmeticType, let N : int, let M :int> __intrinsic_op(Mul) matrix<T,N,M> operator*(T x, matrix<T,N,M> y);\n"; + + // vector-vector (dot product) + sb << "__generic<T : __BuiltinArithmeticType, let N : int> __intrinsic_op(Mul) T operator*(vector<T,N> x, vector<T,N> y);\n"; + + // vector-matrix + sb << "__generic<T : __BuiltinArithmeticType, let N : int, let M : int> __intrinsic_op(Mul) vector<T,M> operator*(vector<T,N> x, matrix<T,N,M> y);\n"; + + // matrix-vector + sb << "__generic<T : __BuiltinArithmeticType, let N : int, let M : int> __intrinsic_op(Mul) vector<T,N> operator*(matrix<T,N,M> x, vector<T,M> y);\n"; + + // matrix-matrix + sb << "__generic<T : __BuiltinArithmeticType, let R : int, let N : int, let C : int> __intrinsic_op(Mul) matrix<T,R,C> operator*(matrix<T,R,N> x, matrix<T,N,C> y);\n"; + + + + // + // TODO(tfoley): Need to handle `RW*` variants of texture types as well... static const struct { char const* name; @@ -1909,6 +1980,7 @@ namespace Slang sb << "__modifier(GLSLPatchModifier) patch;\n"; sb << "__modifier(SimpleModifier) flat;\n"; + sb << "__modifier(SimpleModifier) highp;\n"; glslLibraryCode = sb.ProduceString(); return glslLibraryCode; @@ -1918,10 +1990,12 @@ namespace Slang // - void SlangStdLib::Finalize() + void finalizeShaderLibrary() { - code = String(); stdlibPath = String(); + + coreLibraryCode = String(); + hlslLibraryCode = String(); glslLibraryCode = String(); } diff --git a/source/slang/slang-stdlib.h b/source/slang/slang-stdlib.h index e4ee8cee3..d21b1e7f1 100644 --- a/source/slang/slang-stdlib.h +++ b/source/slang/slang-stdlib.h @@ -5,16 +5,11 @@ namespace Slang { - class SlangStdLib - { - private: - static String code; - public: - static String GetCode(); - static void Finalize(); - }; - + String getCoreLibraryCode(); + String getHLSLLibraryCode(); String getGLSLLibraryCode(); + + void finalizeShaderLibrary(); } #endif
\ No newline at end of file diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index c4944c5a4..e5bfcb923 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -25,8 +25,9 @@ public: bool useCache = false; String cacheDir; - RefPtr<Scope> slangLanguageScope; + RefPtr<Scope> coreLanguageScope; RefPtr<Scope> hlslLanguageScope; + RefPtr<Scope> slangLanguageScope; RefPtr<Scope> glslLanguageScope; List<RefPtr<ProgramSyntaxNode>> loadedModuleCode; @@ -43,15 +44,19 @@ public: // TODO: load these on-demand to avoid parsing // stdlib code for languages the user won't use. - slangLanguageScope = new Scope(); + coreLanguageScope = new Scope(); hlslLanguageScope = new Scope(); - hlslLanguageScope->parent = slangLanguageScope; + hlslLanguageScope->nextSibling = coreLanguageScope; + + slangLanguageScope = new Scope(); + slangLanguageScope->nextSibling = hlslLanguageScope; glslLanguageScope = new Scope(); - glslLanguageScope->parent = slangLanguageScope; + glslLanguageScope->nextSibling = coreLanguageScope; - addBuiltinSource(slangLanguageScope, "stdlib", SlangStdLib::GetCode()); + addBuiltinSource(coreLanguageScope, "core", getCoreLibraryCode()); + addBuiltinSource(hlslLanguageScope, "hlsl", getHLSLLibraryCode()); addBuiltinSource(glslLanguageScope, "glsl", getGLSLLibraryCode()); } @@ -61,7 +66,7 @@ public: // code that we might have allocated and loaded into static // variables (TODO: don't use `static` variables for this stuff) - SlangStdLib::Finalize(); + finalizeShaderLibrary(); // Ditto for our type represnetation stuff diff --git a/source/slang/stmt-defs.h b/source/slang/stmt-defs.h index c5bcda63b..9dea40fbf 100644 --- a/source/slang/stmt-defs.h +++ b/source/slang/stmt-defs.h @@ -16,6 +16,8 @@ SYNTAX_CLASS(BlockStmt, ScopeStmt) SYNTAX_FIELD(RefPtr<StatementSyntaxNode>, body); END_SYNTAX_CLASS() +// A statement that we aren't going to parse or check, because +// we want to let a downstream compiler handle any issues SYNTAX_CLASS(UnparsedStmt, StatementSyntaxNode) // The tokens that were contained between `{` and `}` FIELD(List<Token>, tokens) @@ -71,6 +73,7 @@ SIMPLE_SYNTAX_CLASS(DefaultStmt, CaseStmtBase) ABSTRACT_SYNTAX_CLASS(LoopStmt, BreakableStmt) END_SYNTAX_CLASS() +// A `for` statement SYNTAX_CLASS(ForStatementSyntaxNode, LoopStmt) SYNTAX_FIELD(RefPtr<StatementSyntaxNode>, InitialStatement) SYNTAX_FIELD(RefPtr<ExpressionSyntaxNode>, SideEffectExpression) @@ -78,6 +81,10 @@ SYNTAX_CLASS(ForStatementSyntaxNode, LoopStmt) SYNTAX_FIELD(RefPtr<StatementSyntaxNode>, Statement) END_SYNTAX_CLASS() +// A `for` statement in a language that doesn't restrict the scope +// of the loop variable to the body. +SYNTAX_CLASS(UnscopedForStmt, ForStatementSyntaxNode); +END_SYNTAX_CLASS() SYNTAX_CLASS(WhileStatementSyntaxNode, LoopStmt) SYNTAX_FIELD(RefPtr<ExpressionSyntaxNode>, Predicate) diff --git a/source/slang/syntax.h b/source/slang/syntax.h index 1704c1224..de5467c71 100644 --- a/source/slang/syntax.h +++ b/source/slang/syntax.h @@ -176,6 +176,7 @@ namespace Slang Int, UInt, UInt64, + Half, Float, Double, }; @@ -690,22 +691,6 @@ namespace Slang LookupMask mask = LookupMask::All; }; - enum class Operator - { - Neg, Not, BitNot, PreInc, PreDec, PostInc, PostDec, - Mul, Div, Mod, - Add, Sub, - Lsh, Rsh, - Eql, Neq, Greater, Less, Geq, Leq, - BitAnd, BitXor, BitOr, - And, - Or, - Sequence, - Select, - Assign = 200, AddAssign, SubAssign, MulAssign, DivAssign, ModAssign, - LshAssign, RshAssign, OrAssign, AndAssign, XorAssign, - }; - // Generate class definition for all syntax classes #define SYNTAX_FIELD(TYPE, NAME) TYPE NAME; #define FIELD(TYPE, NAME) TYPE NAME; diff --git a/tests/rewriter/error0.hlsl b/tests/rewriter/error0.hlsl index 10957e9e5..e42dec23e 100644 --- a/tests/rewriter/error0.hlsl +++ b/tests/rewriter/error0.hlsl @@ -4,14 +4,24 @@ // the input code, we allow the downstream compiler // to detect and report the error, not us... -// This file presents a simple case, where we forgot a semicolon. +// A key goal here is that errors get reported at +// the right source location, ideally including +// all of file, line, and column info. + +// This file used to have a parse error (missing semicolon), +// but at this point we need to parse function bodies, even +// if we don't check them, so we can't avoid reporting that one. +// +// I'm switching it to a type error instead: + +struct S { int x; }; float4 main() : SV_Target { float a = 1.0; - // no semicolon at the end of this line! - float b = 2.0 + // Invalid assignment: + S s = a; float c = a + b; |
