summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Foley <tfoley@nvidia.com>2017-06-28 10:20:16 -0700
committerTim Foley <tfoley@nvidia.com>2017-06-28 11:08:03 -0700
commitd601921b71ed44835e8d4fa6f13ff7aefcf7649d (patch)
tree79b9227ef038d173d780a440035e616dc31104bb
parent4b3936e2983dcecd36a3437bd6c7eef8d5fbbffa (diff)
Actually respect suffixes on numeric literals.
- Add logic to extract the value and suffix from a numeric literal - This duplicates some of the lexing logic, but this is hard to avoid without redundant runtime work - Note that I'm not using and stdlib string-to-number code. This should be more robust once it is working, but it is obviously error prone in the near term. The main up-sides to this are: - We can handle binary integer literals - We can handle hexadecimal floating-point literals without stdlib support - We can hypothetically support digit separators, if we ever wanted - The parser looks at the suffix characters sliced off by the lexer, and tries to pick a type to use for a literal - It uses `NULL` if there is no suffix, to avoid some nasty order dependencies where the stdlib might need to parse a number before it has seen the definition of `int` - Right now I only handle a few cases, so there may be bugs lurking here - The emit logic needs to handle the fact that a literal node in the AST might have a non-default type attached. - Right now I just quickly check for the most likely types, and emit the literal with a matching suffix. This doesn't seem robust if any source language supports a suffix for a type where a target has no corresponding suffix. In the long term some amount of casting is probably required.
-rw-r--r--source/slang/check.cpp4
-rw-r--r--source/slang/diagnostic-defs.h3
-rw-r--r--source/slang/emit.cpp41
-rw-r--r--source/slang/lexer.cpp210
-rw-r--r--source/slang/lexer.h6
-rw-r--r--source/slang/parser.cpp136
-rw-r--r--source/slang/syntax.cpp5
-rw-r--r--source/slang/syntax.h4
-rw-r--r--tests/hlsl/simple/literal-typing.hlsl25
9 files changed, 421 insertions, 13 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 11efb2f7d..dfa726150 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -1887,6 +1887,10 @@ namespace Slang
}
virtual RefPtr<ExpressionSyntaxNode> VisitConstantExpression(ConstantExpressionSyntaxNode *expr) override
{
+ // The expression might already have a type, determined by its suffix
+ if(expr->Type.type)
+ return expr;
+
switch (expr->ConstType)
{
case ConstantExpressionSyntaxNode::ConstantType::Int:
diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h
index f92f43ddb..c85270e08 100644
--- a/source/slang/diagnostic-defs.h
+++ b/source/slang/diagnostic-defs.h
@@ -284,6 +284,9 @@ DIAGNOSTIC(39999, Error, expectedPostfixOperator, "function called as postfix op
DIAGNOSTIC(39999, Error, notEnoughArguments, "not enough arguments to call (got $0, expected $1)")
DIAGNOSTIC(39999, Error, tooManyArguments, "too many arguments to call (got $0, expected $1)")
+DIAGNOSTIC(39999, Error, invalidIntegerLiteralSuffix, "invalid suffix '$0' on integer literal")
+DIAGNOSTIC(39999, Error, invalidFloatingPOintLiteralSuffix, "invalid suffix '$0' on floating-point literal")
+
//
// 4xxxx - IL code generation.
//
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 5272204f6..9a1d44641 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -219,6 +219,8 @@ static void Emit(EmitContext* context, double value)
Emit(context, buffer);
}
+static void emitTokenWithLocation(EmitContext* context, Token const& token);
+
// Expressions
// Determine if an expression should not be emitted when it is the base of
@@ -900,14 +902,53 @@ static void EmitExprWithPrecedence(EmitContext* context, RefPtr<ExpressionSyntax
{
needClose = MaybeEmitParens(context, outerPrec, kPrecedence_Atomic);
+ char const* suffix = "";
+ auto type = litExpr->Type.type;
switch (litExpr->ConstType)
{
case ConstantExpressionSyntaxNode::ConstantType::Int:
+ if(!type)
+ {
+ // Special case for "rewrite" mode
+ emitTokenWithLocation(context, litExpr->token);
+ break;
+ }
+ if(type->Equals(ExpressionType::GetInt()))
+ {}
+ else if(type->Equals(ExpressionType::GetUInt()))
+ {
+ suffix = "u";
+ }
+ else
+ {
+ assert(!"unimplemented");
+ }
Emit(context, litExpr->IntValue);
+ Emit(context, suffix);
break;
+
+
case ConstantExpressionSyntaxNode::ConstantType::Float:
+ if(!type)
+ {
+ // Special case for "rewrite" mode
+ emitTokenWithLocation(context, litExpr->token);
+ break;
+ }
+ if(type->Equals(ExpressionType::GetFloat()))
+ {}
+ else if(type->Equals(ExpressionType::getDoubleType()))
+ {
+ suffix = "l";
+ }
+ else
+ {
+ assert(!"unimplemented");
+ }
Emit(context, litExpr->FloatValue);
+ Emit(context, suffix);
break;
+
case ConstantExpressionSyntaxNode::ConstantType::Bool:
Emit(context, litExpr->IntValue ? "true" : "false");
break;
diff --git a/source/slang/lexer.cpp b/source/slang/lexer.cpp
index 73fbb9605..786376baf 100644
--- a/source/slang/lexer.cpp
+++ b/source/slang/lexer.cpp
@@ -415,25 +415,34 @@ namespace Slang
return tokenType;
}
- static bool maybeLexNumberExponent(Lexer* lexer, int base)
+ static bool isNumberExponent(char c, int base)
{
- switch( peek(lexer) )
+ switch( c )
{
default:
return false;
case 'e': case 'E':
if(base != 10) return false;
- advance(lexer);
break;
case 'p': case 'P':
if(base != 16) return false;
- advance(lexer);
break;
}
- // we saw an exponent marker, so we must
+ return true;
+ }
+
+ static bool maybeLexNumberExponent(Lexer* lexer, int base)
+ {
+ if(!isNumberExponent(peek(lexer), base))
+ return false;
+
+ // we saw an exponent marker
+ advance(lexer);
+
+ // Now start to read the exponent
switch( peek(lexer) )
{
case '+': case '-':
@@ -482,6 +491,197 @@ namespace Slang
return tokenType;
}
+ static int maybeReadDigit(char const** ioCursor, int base)
+ {
+ auto& cursor = *ioCursor;
+
+ for(;;)
+ {
+ int digitVal = 0;
+ int c = *cursor;
+ switch(c)
+ {
+ default:
+ return -1;
+
+ // TODO: need to decide on digit separator characters
+ case '_':
+ cursor++;
+ continue;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ cursor++;
+ return c - '0';
+
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ if(base > 10)
+ {
+ cursor++;
+ return c - 'a';
+ }
+ return -1;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ if(base > 10)
+ {
+ cursor++;
+ return c - 'A';
+ }
+ return -1;
+ }
+ }
+ }
+
+ static int readOptionalBase(char const** ioCursor)
+ {
+ auto& cursor = *ioCursor;
+ if( *cursor == '0' )
+ {
+ cursor++;
+ switch(*cursor)
+ {
+ case 'x': case 'X':
+ cursor++;
+ return 16;
+
+ case 'b': case 'B':
+ cursor++;
+ return 2;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return 8;
+
+ default:
+ return 10;
+ }
+ }
+
+ return 10;
+ }
+
+
+
+ IntegerLiteralValue getIntegerLiteralValue(Token const& token, String* outSuffix)
+ {
+ IntegerLiteralValue value = 0;
+
+ char const* cursor = token.Content.begin();
+ char const* end = token.Content.end();
+
+ int base = readOptionalBase(&cursor);
+
+ for( ;;)
+ {
+ int digit = maybeReadDigit(&cursor, base);
+ if(digit < 0)
+ break;
+
+ value = value*base + digit;
+ }
+
+ if(outSuffix)
+ {
+ *outSuffix = String(cursor, end);
+ }
+
+ return value;
+ }
+
+ FloatingPointLiteralValue getFloatingPointLiteralValue(Token const& token, String* outSuffix)
+ {
+ FloatingPointLiteralValue value = 0;
+
+ char const* cursor = token.Content.begin();
+ char const* end = token.Content.end();
+
+ int radix = readOptionalBase(&cursor);
+
+ bool seenDot = false;
+ FloatingPointLiteralValue divisor = 1;
+ for( ;;)
+ {
+ if(*cursor == '.')
+ {
+ cursor++;
+ seenDot = true;
+ continue;
+ }
+
+ int digit = maybeReadDigit(&cursor, radix);
+ if(digit < 0)
+ break;
+
+ value = value*radix + digit;
+
+ if(seenDot)
+ {
+ divisor *= radix;
+ }
+ }
+
+ // Now read optional exponent
+ if(isNumberExponent(*cursor, radix))
+ {
+ cursor++;
+
+ bool exponentIsNegative = false;
+ switch(*cursor)
+ {
+ default:
+ break;
+
+ case '-':
+ exponentIsNegative = true;
+ cursor++;
+ break;
+
+ case '+':
+ cursor++;
+ break;
+ }
+
+ int exponentRadix = 10;
+ int exponent = 0;
+
+ for(;;)
+ {
+ int digit = maybeReadDigit(&cursor, exponentRadix);
+ if(digit < 0)
+ break;
+
+ exponent = exponent*exponentRadix + digit;
+ }
+
+ FloatingPointLiteralValue exponentBase = 10;
+ if(radix == 16)
+ {
+ exponentBase = 2;
+ }
+
+ FloatingPointLiteralValue exponentValue = pow(exponentBase, exponent);
+
+ if( exponentIsNegative )
+ {
+ divisor *= exponentValue;
+ }
+ else
+ {
+ value *= exponentValue;
+ }
+ }
+
+ value /= divisor;
+
+ if(outSuffix)
+ {
+ *outSuffix = String(cursor, end);
+ }
+
+ return value;
+ }
+
static void lexStringLiteralBody(Lexer* lexer, char quote)
{
for(;;)
diff --git a/source/slang/lexer.h b/source/slang/lexer.h
index 5bcfc0f4a..53bfc0999 100644
--- a/source/slang/lexer.h
+++ b/source/slang/lexer.h
@@ -91,6 +91,12 @@ namespace Slang
// Helper routines for extracting values from tokens
String getStringLiteralTokenValue(Token const& token);
String getFileNameTokenValue(Token const& token);
+
+ typedef unsigned long long IntegerLiteralValue;
+ typedef double FloatingPointLiteralValue;
+
+ IntegerLiteralValue getIntegerLiteralValue(Token const& token, String* outSuffix = 0);
+ FloatingPointLiteralValue getFloatingPointLiteralValue(Token const& token, String* outSuffix = 0);
}
#endif \ No newline at end of file
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index 5e93d63cb..6437deca2 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -3214,22 +3214,140 @@ namespace Slang
}
case TokenType::IntegerLiteral:
- case TokenType::FloatingPointLiteral:
{
RefPtr<ConstantExpressionSyntaxNode> constExpr = new ConstantExpressionSyntaxNode();
- auto token = parser->tokenReader.AdvanceToken();
parser->FillPosition(constExpr.Ptr());
- if (token.Type == TokenType::IntegerLiteral)
+
+ auto token = parser->tokenReader.AdvanceToken();
+ constExpr->token = token;
+
+ String suffix;
+ IntegerLiteralValue value = getIntegerLiteralValue(token, &suffix);
+
+ // Look at any suffix on the value
+ char const* suffixCursor = suffix.begin();
+
+ RefPtr<ExpressionType> suffixType = nullptr;
+ if( suffixCursor && *suffixCursor )
{
- constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Int;
- constExpr->IntValue = StringToInt(token.Content);
+ int lCount = 0;
+ int uCount = 0;
+ int unknownCount = 0;
+ while(*suffixCursor)
+ {
+ switch( *suffixCursor++ )
+ {
+ case 'l': case 'L':
+ lCount++;
+ break;
+
+ case 'u': case 'U':
+ uCount++;
+ break;
+
+ default:
+ unknownCount++;
+ break;
+ }
+ }
+
+ if(unknownCount)
+ {
+ parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix);
+ suffixType = ExpressionType::GetError();
+ }
+ // `u` or `ul` suffix -> `uint`
+ else if(uCount == 1 && (lCount <= 1))
+ {
+ suffixType = ExpressionType::GetUInt();
+ }
+ // `l` suffix on integer -> `int` (== `long`)
+ else if(lCount == 1 && !uCount)
+ {
+ suffixType = ExpressionType::GetInt();
+ }
+ // TODO: probably need `ll` and `ull`
+ // TODO: are there other suffixes we need to handle?
+ else
+ {
+ parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix);
+ suffixType = ExpressionType::GetError();
+ }
}
- else if (token.Type == TokenType::FloatingPointLiteral)
+
+ constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Int;
+ constExpr->IntValue = value;
+ constExpr->Type = suffixType;
+
+ return constExpr;
+ }
+
+
+ case TokenType::FloatingPointLiteral:
+ {
+ RefPtr<ConstantExpressionSyntaxNode> constExpr = new ConstantExpressionSyntaxNode();
+ parser->FillPosition(constExpr.Ptr());
+
+ auto token = parser->tokenReader.AdvanceToken();
+ constExpr->token = token;
+
+ String suffix;
+ FloatingPointLiteralValue value = getFloatingPointLiteralValue(token, &suffix);
+
+ // Look at any suffix on the value
+ char const* suffixCursor = suffix.begin();
+
+ RefPtr<ExpressionType> suffixType = nullptr;
+ if( suffixCursor && *suffixCursor )
{
- constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Float;
- constExpr->FloatValue = (FloatingPointLiteralValue) StringToDouble(token.Content);
+ int fCount = 0;
+ int lCount = 0;
+ int unknownCount = 0;
+ while(*suffixCursor)
+ {
+ switch( *suffixCursor++ )
+ {
+ case 'f': case 'F':
+ fCount++;
+ break;
+
+ case 'l': case 'L':
+ lCount++;
+ break;
+
+ default:
+ unknownCount++;
+ break;
+ }
+ }
+
+ if(unknownCount)
+ {
+ parser->sink->diagnose(token, Diagnostics::invalidFloatingPOintLiteralSuffix, suffix);
+ suffixType = ExpressionType::GetError();
+ }
+ // `f` suffix -> `float`
+ if(fCount == 1 && !lCount)
+ {
+ suffixType = ExpressionType::GetFloat();
+ }
+ // `l` or `lf` suffix on float -> `double`
+ else if(lCount == 1 && (fCount <= 1))
+ {
+ suffixType = ExpressionType::getDoubleType();
+ }
+ // TODO: are there other suffixes we need to handle?
+ else
+ {
+ parser->sink->diagnose(token, Diagnostics::invalidFloatingPOintLiteralSuffix, suffix);
+ suffixType = ExpressionType::GetError();
+ }
}
+ constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Float;
+ constExpr->FloatValue = value;
+ constExpr->Type = suffixType;
+
return constExpr;
}
@@ -3237,6 +3355,7 @@ namespace Slang
{
RefPtr<ConstantExpressionSyntaxNode> constExpr = new ConstantExpressionSyntaxNode();
auto token = parser->tokenReader.AdvanceToken();
+ constExpr->token = token;
parser->FillPosition(constExpr.Ptr());
constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::String;
@@ -3269,6 +3388,7 @@ namespace Slang
{
RefPtr<ConstantExpressionSyntaxNode> constExpr = new ConstantExpressionSyntaxNode();
auto token = parser->tokenReader.AdvanceToken();
+ constExpr->token = token;
parser->FillPosition(constExpr.Ptr());
constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Bool;
constExpr->IntValue = token.Content == "true" ? 1 : 0;
diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp
index f78d3c8ec..6b9a74f7d 100644
--- a/source/slang/syntax.cpp
+++ b/source/slang/syntax.cpp
@@ -1397,6 +1397,11 @@ namespace Slang
return sBuiltinTypes[(int)BaseType::Float].GetValue().Ptr();
}
+ ExpressionType* ExpressionType::getDoubleType()
+ {
+ return sBuiltinTypes[(int)BaseType::Double].GetValue().Ptr();
+ }
+
ExpressionType* ExpressionType::GetInt()
{
return sBuiltinTypes[(int)BaseType::Int].GetValue().Ptr();
diff --git a/source/slang/syntax.h b/source/slang/syntax.h
index 7289719f4..81d6f8e01 100644
--- a/source/slang/syntax.h
+++ b/source/slang/syntax.h
@@ -375,6 +375,7 @@ namespace Slang
UInt,
UInt64,
Float,
+ Double,
#if 0
Texture2D = 48,
TextureCube = 49,
@@ -485,6 +486,7 @@ namespace Slang
static ExpressionType* GetBool();
static ExpressionType* GetFloat();
+ static ExpressionType* getDoubleType();
static ExpressionType* GetInt();
static ExpressionType* GetUInt();
static ExpressionType* GetVoid();
@@ -1916,6 +1918,8 @@ namespace Slang
class ConstantExpressionSyntaxNode : public ExpressionSyntaxNode
{
public:
+ Token token;
+
enum class ConstantType
{
Int,
diff --git a/tests/hlsl/simple/literal-typing.hlsl b/tests/hlsl/simple/literal-typing.hlsl
new file mode 100644
index 000000000..71acb0d92
--- /dev/null
+++ b/tests/hlsl/simple/literal-typing.hlsl
@@ -0,0 +1,25 @@
+//TEST:COMPARE_HLSL: -target dxbc-assembly -profile cs_5_0 -entry main
+
+// Confirm that we get the typing of literal suffixes correct
+
+// A type created to cause type-checking failures downstream
+struct Bad { int bad; };
+
+// We define two overloads for `foo()`. The "right" one takes
+// an unsigned integer, and returns it. The "wrong" one takes
+// a signed integer and returns a `Bad`.
+
+uint foo(uint x) { return x; }
+Bad foo(int x) { Bad b; b.bad = x; return b; }
+
+// The shader entry point will call `foo()` on a literal
+// with a suffix that should make it unsigned, so that
+// we either respect the suffix and call the right overload,
+// or ignore it and call the wrong one.
+
+RWStructuredBuffer<uint> b;
+[numthreads(32,1,1)]
+void main(uint3 tid : SV_DispatchThreadID)
+{
+ b[tid.x] = foo(99u);
+} \ No newline at end of file