diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/check.cpp | 16 | ||||
| -rw-r--r-- | source/slang/diagnostic-defs.h | 14 | ||||
| -rw-r--r-- | source/slang/preprocessor.cpp | 105 |
3 files changed, 123 insertions, 12 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 95a958495..42bcfe4b5 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -1876,7 +1876,7 @@ namespace Slang } return true; } - + bool validateAttribute(RefPtr<Attribute> attr) { if(auto numThreadsAttr = attr.As<NumThreadsAttribute>()) @@ -1939,7 +1939,7 @@ namespace Slang entryPointAttr->stage = stage; } - else if ((attr.As<DomainAttribute>()) || + else if ((attr.As<DomainAttribute>()) || (attr.As<MaxTessFactorAttribute>()) || (attr.As<OutputTopologyAttribute>()) || (attr.As<PartitioningAttribute>()) || @@ -1964,7 +1964,7 @@ namespace Slang // Has no args SLANG_ASSERT(attr->args.Count() == 0); } - else + else { if(attr->args.Count() == 0) { @@ -3639,8 +3639,9 @@ namespace Slang auto prevResultType = GetResultType(prevFuncDeclRef); if (!resultType->Equals(prevResultType)) { - // Bad dedeclaration - getSink()->diagnose(funcDecl, Diagnostics::unimplemented, "redeclaration has a different return type"); + // Bad redeclaration + getSink()->diagnose(funcDecl, Diagnostics::functionRedeclarationWithDifferentReturnType, funcDecl->getName(), resultType, prevResultType); + getSink()->diagnose(prevFuncDecl, Diagnostics::seePreviousDeclarationOf, funcDecl->getName()); // Don't bother emitting other errors at this point break; @@ -3671,7 +3672,8 @@ namespace Slang if (funcDecl->Body && prevFuncDecl->Body) { // Redefinition - getSink()->diagnose(funcDecl, Diagnostics::unimplemented, "function redefinition"); + getSink()->diagnose(funcDecl, Diagnostics::functionRedefinition, funcDecl->getName()); + getSink()->diagnose(prevFuncDecl, Diagnostics::seePreviousDefinitionOf, funcDecl->getName()); // Don't bother emitting other errors break; @@ -8379,7 +8381,7 @@ namespace Slang // TODO: We currently do minimal checking here, but this is the // right place to perform the following validation checks: // - + // * Are the function input/output parameters and result type // all valid for the chosen stage? (e.g., there shouldn't be // an `OutputStream<X>` type in a vertex shader signature) diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h index fd836e946..c0c74c78e 100644 --- a/source/slang/diagnostic-defs.h +++ b/source/slang/diagnostic-defs.h @@ -42,6 +42,7 @@ DIAGNOSTIC(-1, Note, doYouForgetToMakeComponentAccessible, "do you forget to mak DIAGNOSTIC(-1, Note, seeDeclarationOf, "see declaration of '$0'") DIAGNOSTIC(-1, Note, seeOtherDeclarationOf, "see other declaration of '$0'") +DIAGNOSTIC(-1, Note, seePreviousDeclarationOf, "see previous declaration of '$0'") // // 0xxxx - Command line and interaction with host platform APIs. @@ -67,7 +68,7 @@ DIAGNOSTIC( 12, Error, cannotDeduceSourceLanguage, "can't deduce language for DIAGNOSTIC( 13, Error, unknownCodeGenerationTarget, "unknown code generation target '$0'"); DIAGNOSTIC( 14, Error, unknownProfile, "unknown profile '$0'"); DIAGNOSTIC( 15, Error, unknownStage, "unknown stage '$0'"); -DIAGNOSTIC( 16, Error, unknownPassThroughTarget, "unknown pass-through target '$0'"); +DIAGNOSTIC( 16, Error, unknownPassThroughTarget, "unknown pass-through target '$0'"); DIAGNOSTIC( 17, Error, unknownCommandLineOption, "unknown command-line option '$0'"); DIAGNOSTIC( 18, Error, noProfileSpecified, "no profile specified; use the '-profile <profile name>' option"); DIAGNOSTIC( 19, Error, multipleEntryPointsNeedMulitpleProfiles, "when multiple entry points are specified, each must have a profile given (with '-profile') before the '-entry' option"); @@ -130,6 +131,10 @@ DIAGNOSTIC(15403, Error, expectedTokenInMacroParameters, "expected '$0' in macro DIAGNOSTIC(15500, Warning, expectedTokenInMacroArguments, "expected '$0' in macro invocation") DIAGNOSTIC(15501, Error, wrongNumberOfArgumentsToMacro, "wrong number of arguments to macro (expected $0, got $1)") +// 156xx - pragmas +DIAGNOSTIC(15600, Error, expectedPragmaDirectiveName, "expected a name after '#pragma'") +DIAGNOSTIC(15601, Warning, unknownPragmaDirectiveIgnored, "ignoring unknown directive '#pragma $0'") + // 159xx - user-defined error/warning DIAGNOSTIC(15900, Error, userDefinedError, "#error: $0") DIAGNOSTIC(15901, Warning, userDefinedWarning, "#warning: $0") @@ -160,7 +165,6 @@ DIAGNOSTIC(20011, Error, unexpectedColon, "unexpected ':'.") // 3xxxx - Semantic analysis // -DIAGNOSTIC(30001, Error, functionRedefinitionWithArgList, "'$0$1': function redefinition.") DIAGNOSTIC(30002, Error, parameterAlreadyDefined, "parameter '$0' already defined.") DIAGNOSTIC(30003, Error, breakOutsideLoop, "'break' must appear inside loop constructs.") DIAGNOSTIC(30004, Error, continueOutsideLoop, "'continue' must appear inside loop constructs.") @@ -203,9 +207,15 @@ DIAGNOSTIC(30052, Error, invalidSwizzleExpr, "invalid swizzle pattern '$0' on ty DIAGNOSTIC(30100, Error, staticRefToNonStaticMember, "type '$0' cannot be used to refer to non-static member '$1'") +DIAGNOSTIC(30201, Error, functionRedefinition, "function '$0' already has a body") +DIAGNOSTIC(30202, Error, functionRedeclarationWithDifferentReturnType, "function '$0' declared to return '$1' was previously declared to return '$2'") + + DIAGNOSTIC(33070, Error, expectedFunction, "expression preceding parenthesis of apparent call must have function type.") DIAGNOSTIC(33071, Error, expectedAStringLiteral, "expected a string literal") + + // Attributes DIAGNOSTIC(31000, Error, unknownAttributeName, "unknown attribute '$0'") DIAGNOSTIC(31001, Error, attributeArgumentCountMismatch, "attribute '$0' expects $1 arguments ($2 provided)") diff --git a/source/slang/preprocessor.cpp b/source/slang/preprocessor.cpp index 3ec44fc28..71c6fd4bb 100644 --- a/source/slang/preprocessor.cpp +++ b/source/slang/preprocessor.cpp @@ -197,6 +197,11 @@ struct Preprocessor // The translation unit that is being parsed TranslationUnitRequest* translationUnit; + // Any paths that have issued `#pragma once` directives to + // stop them from being included again. + HashSet<String> pragmaOncePaths; + + TranslationUnitRequest* getTranslationUnit() { return translationUnit; @@ -1608,6 +1613,12 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context) // a switch of input stream expectEndOfDirective(context); + // Check whether we've previously included this file and seen a `#pragma once` directive + if(context->preprocessor->pragmaOncePaths.Contains(foundPath)) + { + return; + } + // Push the new file onto our stack of input streams // TODO(tfoley): check if we have made our include stack too deep @@ -1818,12 +1829,100 @@ static void HandleLineDirective(PreprocessorDirectiveContext* context) inputStream->primaryStream->lexer.startOverridingSourceLocations(newLoc); } +#define SLANG_PRAGMA_DIRECTIVE_CALLBACK(NAME) \ + void NAME(PreprocessorDirectiveContext* context, Token subDirectiveToken) + +// Callback interface used by `#pragma` directives +typedef SLANG_PRAGMA_DIRECTIVE_CALLBACK((*PragmaDirectiveCallback)); + +SLANG_PRAGMA_DIRECTIVE_CALLBACK(handleUnknownPragmaDirective) +{ + GetSink(context)->diagnose(subDirectiveToken, Diagnostics::unknownPragmaDirectiveIgnored, subDirectiveToken.getName()); + SkipToEndOfLine(context); + return; +} + +SLANG_PRAGMA_DIRECTIVE_CALLBACK(handlePragmaOnceDirective) +{ + // We need to identify the path of the file we are preprocessing, + // so that we can avoid including it again. + // + // Note: for now we are doing a very simplistic check where + // we use the raw file path as the key for our duplicate checking. + // + // TODO: a more refined implementation should probably apply Unicode + // normalization and case-folding to the path, and then use that + // plus a hash of the file contents to determine whether things + // represent the "same" file. + // + // TODO: even for our simplistic implementation, we need to add + // logic to deal with `../` segments in path names to detect + // trivial cases of the "same" path. + // + auto directiveLoc = GetDirectiveLoc(context); + auto expandedDirectiveLoc = context->preprocessor->translationUnit->compileRequest->getSourceManager()->expandSourceLoc(directiveLoc); + String pathIssuedFrom = expandedDirectiveLoc.getSpellingPath(); + + context->preprocessor->pragmaOncePaths.Add(pathIssuedFrom); +} + +// Information about a specific `#pragma` directive +struct PragmaDirective +{ + // name of the directive + char const* name; + + // Callback to handle the directive + PragmaDirectiveCallback callback; +}; + +// A simple array of all the `#pragma` directives we know how to handle. +static const PragmaDirective kPragmaDirectives[] = +{ + { "once", &handlePragmaOnceDirective }, + + { NULL, NULL }, +}; + +static const PragmaDirective kUnknownPragmaDirective = { + NULL, &handleUnknownPragmaDirective, +}; + +// Look up the `#pragma` directive with the given name. +static PragmaDirective const* findPragmaDirective(String const& name) +{ + char const* nameStr = name.Buffer(); + for (int ii = 0; kPragmaDirectives[ii].name; ++ii) + { + if (strcmp(kPragmaDirectives[ii].name, nameStr) != 0) + continue; + + return &kPragmaDirectives[ii]; + } + + return &kUnknownPragmaDirective; +} + // Handle a `#pragma` directive static void HandlePragmaDirective(PreprocessorDirectiveContext* context) { - // TODO(tfoley): figure out which pragmas to parse, - // and which to pass along - SkipToEndOfLine(context); + // Try to read the sub-directive name. + Token subDirectiveToken = PeekRawToken(context); + + // The sub-directive had better be an identifier + if (subDirectiveToken.type != TokenType::Identifier) + { + GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::expectedPragmaDirectiveName); + SkipToEndOfLine(context); + return; + } + AdvanceRawToken(context); + + // Look up the handler for the sub-directive. + PragmaDirective const* subDirective = findPragmaDirective(subDirectiveToken.getName()->text); + + // Apply the sub-directive-specific callback + (subDirective->callback)(context, subDirectiveToken); } // Handle a `#version` directive |
