@@ -16,6 +16,7 @@ import (
16
16
"path/filepath"
17
17
"slices"
18
18
"strings"
19
+ "sync"
19
20
20
21
"cmd/compile/internal/abi"
21
22
"cmd/compile/internal/base"
@@ -5429,6 +5430,118 @@ func isStandardLibraryFile(filename string) bool {
5429
5430
return false
5430
5431
}
5431
5432
5433
+ // Suppression directives to skip integer overflow checks on specific source lines.
5434
+ // If a line contains one of these markers, or the immediately preceding line contains it,
5435
+ // arithmetic overflow checks for that line will be skipped.
5436
+ const (
5437
+ // Match either with or without a leading '@' in comments
5438
+ overflowSuppressionDirectivePrimary = "overflow_false_positive"
5439
+ )
5440
+
5441
+ // cache mapping filename -> set of line numbers that contain the suppression directive
5442
+ var overflowSuppressionCache sync.Map // map[string]map[int]struct{}
5443
+
5444
+ // hasOverflowSuppression reports whether the given position has a nearby suppression directive.
5445
+ // It returns true if the exact line or the immediately preceding line contains the directive.
5446
+ func hasOverflowSuppression (pos src.XPos ) bool {
5447
+ if ! pos .IsKnown () {
5448
+ return false
5449
+ }
5450
+ p := base .Ctxt .PosTable .Pos (pos )
5451
+ filename := p .Filename ()
5452
+ intLine := int (p .Line ())
5453
+ if filename == "" || intLine <= 0 {
5454
+ return false
5455
+ }
5456
+
5457
+ // Load or populate the per-file directive line set.
5458
+ var lineSet map [int ]struct {}
5459
+ if v , ok := overflowSuppressionCache .Load (filename ); ok {
5460
+ lineSet , _ = v .(map [int ]struct {})
5461
+ } else {
5462
+ // Build the set once per file.
5463
+ data , err := os .ReadFile (filename )
5464
+ if err != nil {
5465
+ return false
5466
+ }
5467
+ lines := strings .Split (string (data ), "\n " )
5468
+ set := make (map [int ]struct {}, 8 )
5469
+ for i , s := range lines {
5470
+ if strings .Contains (s , overflowSuppressionDirectivePrimary ) {
5471
+ // Lines are 1-indexed in src.Pos
5472
+ set [i + 1 ] = struct {}{}
5473
+ }
5474
+ }
5475
+ overflowSuppressionCache .Store (filename , set )
5476
+ lineSet = set
5477
+ }
5478
+
5479
+ if lineSet == nil {
5480
+ return false
5481
+ }
5482
+ if _ , ok := lineSet [intLine ]; ok {
5483
+ return true
5484
+ }
5485
+ if intLine > 1 {
5486
+ if _ , ok := lineSet [intLine - 1 ]; ok {
5487
+ return true
5488
+ }
5489
+ }
5490
+ return false
5491
+ }
5492
+
5493
+ // Suppress truncation detection when a directive is present on the same or previous line.
5494
+ const (
5495
+ // Match either with or without a leading '@' in comments
5496
+ truncationSuppressionDirectivePrimary = "truncation_false_positive"
5497
+ )
5498
+
5499
+ var truncationSuppressionCache sync.Map // map[string]map[int]struct{}
5500
+
5501
+ func hasTruncationSuppression (pos src.XPos ) bool {
5502
+ if ! pos .IsKnown () {
5503
+ return false
5504
+ }
5505
+ p := base .Ctxt .PosTable .Pos (pos )
5506
+ filename := p .Filename ()
5507
+ intLine := int (p .Line ())
5508
+ if filename == "" || intLine <= 0 {
5509
+ return false
5510
+ }
5511
+
5512
+ var lineSet map [int ]struct {}
5513
+ if v , ok := truncationSuppressionCache .Load (filename ); ok {
5514
+ lineSet , _ = v .(map [int ]struct {})
5515
+ } else {
5516
+ data , err := os .ReadFile (filename )
5517
+ if err != nil {
5518
+ return false
5519
+ }
5520
+ lines := strings .Split (string (data ), "\n " )
5521
+ set := make (map [int ]struct {}, 8 )
5522
+ for i , s := range lines {
5523
+ if strings .Contains (s , truncationSuppressionDirectivePrimary ) {
5524
+ set [i + 1 ] = struct {}{}
5525
+ }
5526
+ }
5527
+ truncationSuppressionCache .Store (filename , set )
5528
+ lineSet = set
5529
+ }
5530
+
5531
+ if lineSet == nil {
5532
+ return false
5533
+ }
5534
+ if _ , ok := lineSet [intLine ]; ok {
5535
+ return true
5536
+ }
5537
+ if intLine > 1 {
5538
+ if _ , ok := lineSet [intLine - 1 ]; ok {
5539
+ return true
5540
+ }
5541
+ }
5542
+ return false
5543
+ }
5544
+
5432
5545
// shouldCheckOverflow returns true if overflow detection should be applied for this operation.
5433
5546
// It checks if the package should be excluded from overflow detection and if the type is supported.
5434
5547
func (s * state ) shouldCheckOverflow (typ * types.Type ) bool {
@@ -5484,7 +5597,6 @@ func (s *state) shouldCheckTruncation(n ir.Node, fromType, toType *types.Type) b
5484
5597
return false
5485
5598
}
5486
5599
5487
-
5488
5600
// Check truncation for integer types in these cases:
5489
5601
// 1. Target type is smaller than source type (traditional truncation)
5490
5602
// 2. Same size but different signedness (problematic conversions)
@@ -5539,6 +5651,11 @@ func (s *state) checkTypeTruncation(n ir.Node, value *ssa.Value, fromType, toTyp
5539
5651
}
5540
5652
}
5541
5653
5654
+ // Allow source-level suppression using the same directive key.
5655
+ if hasTruncationSuppression (n .Pos ()) {
5656
+ return s .newValue1 (op , toType , value )
5657
+ }
5658
+
5542
5659
// Perform the conversion first
5543
5660
result := s .newValue1 (op , toType , value )
5544
5661
@@ -5631,6 +5748,10 @@ func (s *state) intAdd(n ir.Node, a, b *ssa.Value) *ssa.Value {
5631
5748
}
5632
5749
}
5633
5750
5751
+ if hasOverflowSuppression (n .Pos ()) {
5752
+ return s .newValue2 (s .ssaOp (n .Op (), n .Type ()), a .Type , a , b )
5753
+ }
5754
+
5634
5755
if ! s .shouldCheckOverflow (n .Type ()) {
5635
5756
return s .newValue2 (s .ssaOp (n .Op (), n .Type ()), a .Type , a , b )
5636
5757
}
@@ -5702,6 +5823,10 @@ func (s *state) intSub(n ir.Node, a, b *ssa.Value) *ssa.Value {
5702
5823
}
5703
5824
}
5704
5825
5826
+ if hasOverflowSuppression (n .Pos ()) {
5827
+ return s .newValue2 (s .ssaOp (n .Op (), n .Type ()), a .Type , a , b )
5828
+ }
5829
+
5705
5830
if ! s .shouldCheckOverflow (n .Type ()) {
5706
5831
return s .newValue2 (s .ssaOp (n .Op (), n .Type ()), a .Type , a , b )
5707
5832
}
@@ -5773,6 +5898,10 @@ func (s *state) intMul(n ir.Node, a, b *ssa.Value) *ssa.Value {
5773
5898
}
5774
5899
}
5775
5900
5901
+ if hasOverflowSuppression (n .Pos ()) {
5902
+ return s .newValue2 (s .ssaOp (n .Op (), n .Type ()), a .Type , a , b )
5903
+ }
5904
+
5776
5905
if ! s .shouldCheckOverflow (n .Type ()) {
5777
5906
return s .newValue2 (s .ssaOp (n .Op (), n .Type ()), a .Type , a , b )
5778
5907
}
@@ -5845,7 +5974,10 @@ func (s *state) intDiv(n ir.Node, a, b *ssa.Value) *ssa.Value {
5845
5974
s .check (cmp , ir .Syms .Panicdivide )
5846
5975
}
5847
5976
5848
- // If overflow detection is disabled, just perform the division
5977
+ // If overflow detection is suppressed/disabled, just perform the division
5978
+ if hasOverflowSuppression (n .Pos ()) {
5979
+ return s .newValue2 (s .ssaOp (n .Op (), n .Type ()), a .Type , a , b )
5980
+ }
5849
5981
if ! s .shouldCheckOverflow (n .Type ()) {
5850
5982
return s .newValue2 (s .ssaOp (n .Op (), n .Type ()), a .Type , a , b )
5851
5983
}
0 commit comments