Skip to content

Commit

Permalink
Include overflow checks in tuple conversions (#19418)
Browse files Browse the repository at this point in the history
Include overflow checks in tuple conversions
  • Loading branch information
cston authored May 18, 2017
1 parent 56045e8 commit ba0cefe
Show file tree
Hide file tree
Showing 5 changed files with 628 additions and 94 deletions.
34 changes: 3 additions & 31 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;
using System.Collections.Immutable;
using System;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
Expand Down Expand Up @@ -114,40 +113,13 @@ protected BoundExpression CreateConversion(
syntax,
source,
conversion,
IsCheckedConversion(source.Type, destination),
@checked: CheckOverflowAtRuntime,
explicitCastInCode: isCast && !wasCompilerGenerated,
constantValueOpt: constantValue,
type: destination)
{ WasCompilerGenerated = wasCompilerGenerated };
}

private bool IsCheckedConversion(TypeSymbol source, TypeSymbol target)
{
Debug.Assert((object)target != null);

if ((object)source == null || !CheckOverflowAtRuntime)
{
return false;
}

if (source.IsDynamic())
{
return true;
}

SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType;
SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType;

// integral to double or float is never checked, but float/double to integral
// may be checked.
bool sourceIsNumeric = SpecialType.System_Char <= sourceST && sourceST <= SpecialType.System_Double;
bool targetIsNumeric = SpecialType.System_Char <= targetST && targetST <= SpecialType.System_UInt64;

return
sourceIsNumeric && (targetIsNumeric || target.IsPointerType()) ||
targetIsNumeric && source.IsPointerType();
}

protected BoundExpression CreateUserDefinedConversion(SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
{
if (!conversion.IsValid)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ private BoundExpression MakeConversionNode(
{
Debug.Assert(oldNode == null || oldNode.Syntax == syntax);
Debug.Assert((object)rewrittenType != null);
@checked = @checked &&
(_inExpressionLambda && (explicitCastInCode || DistinctSpecialTypes(rewrittenOperand.Type, rewrittenType)) ||
NeedsChecked(rewrittenOperand.Type, rewrittenType));

if (_inExpressionLambda)
{
@checked = @checked && NeedsCheckedConversionInExpressionTree(rewrittenOperand.Type, rewrittenType, explicitCastInCode);
}

switch (conversion.Kind)
{
Expand Down Expand Up @@ -373,53 +375,8 @@ private BoundExpression MakeConversionNode(
type: rewrittenType);
}

private static bool DistinctSpecialTypes(TypeSymbol source, TypeSymbol target)
{
Debug.Assert((object)target != null);

if ((object)source == null)
{
return false;
}

SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType;
SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType;
return sourceST != targetST;
}

private const bool y = true;
private const bool n = false;

private static readonly bool[,] s_needsChecked =
{ // chri08u08i16u16i32u32i64u64
/* chr */
{ n, y, y, y, n, n, n, n, n },
/* i08 */
{ y, n, y, n, y, n, y, n, y },
/* u08 */
{ n, y, n, n, n, n, n, n, n },
/* i16 */
{ y, y, y, n, y, n, y, n, y },
/* u16 */
{ n, y, y, y, n, n, n, n, n },
/* i32 */
{ y, y, y, y, y, n, y, n, y },
/* u32 */
{ y, y, y, y, y, y, n, n, n },
/* i64 */
{ y, y, y, y, y, y, y, n, y },
/* u64 */
{ y, y, y, y, y, y, y, y, n },
/* dec */
{ n, n, n, n, n, n, n, n, n },
/* r32 */
{ y, y, y, y, y, y, y, y, y },
/* r64 */
{ y, y, y, y, y, y, y, y, y },
};

// Determine if the conversion can actually overflow at runtime. If not, no need to generate a checked instruction.
private static bool NeedsChecked(TypeSymbol source, TypeSymbol target)
private static bool NeedsCheckedConversionInExpressionTree(TypeSymbol source, TypeSymbol target, bool explicitCastInCode)
{
Debug.Assert((object)target != null);

Expand All @@ -428,22 +385,20 @@ private static bool NeedsChecked(TypeSymbol source, TypeSymbol target)
return false;
}

if (source.IsDynamic())
{
return true;
}
SpecialType GetUnderlyingSpecialType(TypeSymbol type) =>
type.StrippedType().EnumUnderlyingType().SpecialType;

bool IsInRange(SpecialType type, SpecialType low, SpecialType high) =>
low <= type && type <= high;

SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType;
SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType;
SpecialType sourceST = GetUnderlyingSpecialType(source);
SpecialType targetST = GetUnderlyingSpecialType(target);

// integral to double or float is never checked, but float/double to integral
// may be checked.
bool sourceIsNumeric = SpecialType.System_Char <= sourceST && sourceST <= SpecialType.System_Double;
bool targetIsNumeric = SpecialType.System_Char <= targetST && targetST <= SpecialType.System_UInt64;
return
sourceIsNumeric && target.IsPointerType() ||
targetIsNumeric && source.IsPointerType() ||
sourceIsNumeric && targetIsNumeric && s_needsChecked[sourceST - SpecialType.System_Char, targetST - SpecialType.System_Char];
return (explicitCastInCode || sourceST != targetST) &&
IsInRange(sourceST, SpecialType.System_Char, SpecialType.System_Double) &&
IsInRange(targetST, SpecialType.System_Char, SpecialType.System_UInt64);
}

/// <summary>
Expand Down
Loading

0 comments on commit ba0cefe

Please sign in to comment.