diff --git a/compiler/lexer.nim b/compiler/lexer.nim index ad5dd560c0..769987ba8f 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -316,6 +316,28 @@ proc getNumber(L: var Lexer, result: var Token) = L.bufpos = msgPos lexMessage(L, msgKind, msg % t.literal) + proc checkBitWidth(L: var Lexer, base: NumericalBase, tokType: TokType, + numDigits: int, startpos: int) = + # Check bit width for non-base-10 literals + # Warn if the digit count exceeds what can fit in the target type + let bitsPerDigit = case base + of base2: 1 + of base8: 3 + of base16: 4 + else: raiseAssert "unreachable" + let bitWidth = case tokType + of tkInt8Lit, tkUInt8Lit: 8 + of tkInt16Lit, tkUInt16Lit: 16 + of tkInt32Lit, tkUInt32Lit: 32 + of tkInt64Lit, tkUIntLit, tkIntLit, tkUInt64Lit: 64 + else: raiseAssert "unreachable" + # Maximum digits = ceil(bitWidth / bitsPerDigit) = (bitWidth + bitsPerDigit - 1) div bitsPerDigit + let maxDigits = (bitWidth + bitsPerDigit - 1) div bitsPerDigit + if numDigits > maxDigits: + lexMessageLitNum(L, + "number has " & $numDigits & " digits but type only supports " & + $maxDigits & " digits: '$1'", startpos, warnLongLiterals) + var xi: BiggestInt isBase10 = true @@ -491,6 +513,11 @@ proc getNumber(L: var Lexer, result: var Token) = setNumber result.fNumber, (cast[ptr float64](addr(xi)))[] else: internalError(L.config, getLineInfo(L), "getNumber") + # Check bit width for non-base-10 literals + # Warn if the digit count exceeds what can fit in the target type + if result.base != base10 and result.tokType in {tkIntLit..tkUInt64Lit} and numDigits > 0: + checkBitWidth(L, result.base, result.tokType, numDigits, startpos) + # Bounds checks. Non decimal literals are allowed to overflow the range of # the datatype as long as their pattern don't overflow _bitwise_, hence # below checks of signed sizes against uint*.high is deliberate: diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 397d407077..5bf43592a9 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -93,8 +93,9 @@ type warnBareExcept = "BareExcept", warnImplicitDefaultValue = "ImplicitDefaultValue", warnIgnoredSymbolInjection = "IgnoredSymbolInjection", - warnStdPrefix = "StdPrefix" - warnUnknownNotes = "UnknownNotes" + warnStdPrefix = "StdPrefix", + warnUnknownNotes = "UnknownNotes", + warnLongLiterals = "LongLiterals", warnUser = "User", warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary", # hints @@ -202,6 +203,7 @@ const warnIgnoredSymbolInjection: "$1", warnStdPrefix: "$1 needs the 'std' prefix", warnUnknownNotes: "$1", + warnLongLiterals: "$1", warnUser: "$1", warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable", hintSuccess: "operation successful: $#", diff --git a/tests/lexer/t25074.nim b/tests/lexer/t25074.nim new file mode 100644 index 0000000000..9d3654501d --- /dev/null +++ b/tests/lexer/t25074.nim @@ -0,0 +1,6 @@ +discard """ + matrix: "--warningaserror:longliterals" + errormsg: "number has 64 digits but type only supports 16 digits: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' [LongLiterals]" +""" + +echo $sizeof 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff \ No newline at end of file