Skip to content

SE-0067 (1/5) - Failable initializers for Fixed->Fixed #2963

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 63 additions & 36 deletions stdlib/public/core/FixedPoint.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,6 @@ public struct ${Self}
}
% end

/// Create an instance initialized to `value`.
@_transparent public
init(_ value: ${Self}) { self = value }

% if bits > 8:
/// Creates an integer from its big-endian representation, changing the
Expand Down Expand Up @@ -429,44 +426,72 @@ extension ${Self} {
extension ${Self} : SignedNumber {}
% end

// Construction from other integer types
@_transparent
extension ${Self} {
% for src_ty in all_integer_types(word_bits):
% srcBits = src_ty.bits
% srcSigned = src_ty.is_signed
% Src = src_ty.stdlib_name
% (srcSign, srcExt) = ('s', 'sext') if srcSigned else ('u', 'zext')
% if Self != Src:
public init(_ v: ${Src}) {

%{
import gyb

fixed_fixed_conversion_function = gyb.parse_template("fixed_fixed_conversion_function",
"""
%
let srcNotWord = v._value
let src = value._value
let result: (value: Builtin.${BuiltinName}, error: Builtin.Int1)
%
% if srcBits == bits and srcSign == sign:
let dstNotWord = srcNotWord
% if srcBits == bits and srcSign == sign: # Exact same size/signedness.
result = (src, false._value)
%
% elif srcBits == bits:
let tmp = Builtin.${srcSign}_to_${sign}_checked_conversion_Int${srcBits}(srcNotWord)
Builtin.condfail(tmp.1)
let dstNotWord = tmp.0
% elif srcBits == bits: # Same size, switching signs.
result = Builtin.${srcSign}_to_${sign}_checked_conversion_Int${srcBits}(src)
%
% elif srcBits > bits:
let tmp = Builtin.${srcSign}_to_${sign}_checked_trunc_Int${srcBits}_Int${bits}(srcNotWord)
Builtin.condfail(tmp.1)
let dstNotWord = tmp.0
% elif srcBits > bits: # Larger input, check for truncation.
result = Builtin.${srcSign}_to_${sign}_checked_trunc_Int${srcBits}_Int${bits}(src)
%
% elif srcSigned and not signed:
let tmp = Builtin.s_to_u_checked_conversion_Int${srcBits}(srcNotWord)
Builtin.condfail(tmp.1)
let dstNotWord = Builtin.${srcExt}_Int${srcBits}_Int${bits}(tmp.0)
% elif srcSigned and not signed: # Smaller size input, signed going to unsigned.
let (tmp, signError) = Builtin.s_to_u_checked_conversion_Int${srcBits}(src)
result = (Builtin.${srcExt}_Int${srcBits}_Int${bits}(tmp), signError)
%
% else:
let dstNotWord = Builtin.${srcExt}_Int${srcBits}_Int${bits}(srcNotWord)
% else: # Smaller size input, unsigned to signed or unsigned to unsigned.
result = (Builtin.${srcExt}_Int${srcBits}_Int${bits}(src), false._value)
% end
%
self._value = dstNotWord
%
% if not safelyConvertable:
${error_check}
% end
self._value = result.value
""")
}%

% for src_ty in all_integer_types(word_bits):
% srcBits = src_ty.bits
% srcSigned = src_ty.is_signed
% Src = src_ty.stdlib_name
% (srcSign, srcExt) = ('s', 'sext') if srcSigned else ('u', 'zext')
% safelyConvertable = (srcBits < bits and (srcSign == sign or signed)) or (srcBits == bits and srcSign == sign)

@_transparent
extension ${Self} {

@_transparent
public init(_ value: ${Src}) {
${gyb.execute_template(
fixed_fixed_conversion_function,
error_check="Builtin.condfail(result.error)",
**locals()
)
}
}

% if safelyConvertable:
@available(*, message: "Converting ${Src} to ${Self} will always succeed.")
% end
@_transparent
public init?(exactly value: ${Src}) {
${gyb.execute_template(
fixed_fixed_conversion_function,
error_check="if Bool(result.error) == true { return nil }",
**locals()
)
}
}
% end

% if should_define_truncating_bit_pattern_init(src_ty=src_ty, dst_ty=self_ty):
/// Construct a `${Self}` having the same bitwise representation as
Expand All @@ -476,20 +501,22 @@ extension ${Self} {
@_transparent
public init(truncatingBitPattern: ${Src}) {
%
let srcNotWord = truncatingBitPattern._value
let src = truncatingBitPattern._value
%
% if self_ty.bits == src_ty.bits:
let dstNotWord = srcNotWord
let dstNotWord = src
% else:
let dstNotWord = Builtin.trunc_Int${srcBits}_Int${bits}(srcNotWord)
let dstNotWord = Builtin.trunc_Int${srcBits}_Int${bits}(src)
% end
%
self._value = dstNotWord
}

% end
}
% end

extension ${Self} {
/// Construct a `${Self}` having the same memory representation as
/// the `${OtherSelf}` `bitPattern`. No range or overflow checking
/// occurs, and the resulting `${Self}` may not have the same numeric
Expand Down
64 changes: 0 additions & 64 deletions validation-test/stdlib/FixedPointArithmeticTraps.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -299,69 +299,5 @@ FixedPointArithmeticTraps.test("${description}/${IntTy}/Max") {

% end

// FIXME: these tests should be more thorough, and test all combinations of
// types and error conditions.
var FixedPointTruncationTraps = TestSuite("FixedPointTruncationTraps")

FixedPointTruncationTraps.test("SignedToSignedTruncation/dest=sign-overflow") {
// Test that we check if we overflow on the sign bit.
var x = getInt16(128)
expectCrashLater()
var result = Int8(x)
_blackHole(result)
}

FixedPointTruncationTraps.test("SignedToUnsignedTruncation/src=-1") {
var x = getInt32(-1)
expectCrashLater()
var result = UInt8(x)
_blackHole(result)
}

FixedPointTruncationTraps.test("SignedToUnsignedSameSize/src=min") {
var x = getInt8(-128)
expectCrashLater()
var result = UInt16(x)
_blackHole(result)
}


FixedPointTruncationTraps.test("SignedToUnsignedTruncation/src=max") {
var x = getInt32(0xFFFFFFF)
expectCrashLater()
var result = UInt16(x)
_blackHole(result)
}

FixedPointTruncationTraps.test("UnsignedToSignedTruncation/dest=sign-overflow") {
// Test that we check if we overflow on the sign bit.
var x = getUInt16(128)
expectCrashLater()
var result = Int8(x)
_blackHole(result)
}

FixedPointTruncationTraps.test("UnsignedToUnsignedTruncation/src=max") {
var x = getUInt32(0xFFFFFFFF)
expectCrashLater()
var result = UInt16(x)
_blackHole(result)
}

// Same size conversions.
FixedPointTruncationTraps.test("SignedToUnsignedSameSize") {
var x = getInt8(-2)
expectCrashLater()
var result = UInt8(x)
_blackHole(result)
}

FixedPointTruncationTraps.test("UnsignedToSignedSameSize") {
var x = getUInt8(128)
expectCrashLater()
var result = Int8(x)
_blackHole(result)
}

runAllTests()

122 changes: 122 additions & 0 deletions validation-test/stdlib/FixedPointConversion.swift.gyb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: %S/../../utils/gyb %s -o %t/FixedPointConversion.swift
// RUN: %S/../../utils/line-directive %t/FixedPointConversion.swift -- %target-build-swift %t/FixedPointConversion.swift -o %t/a.out_Debug
// RUN: %S/../../utils/line-directive %t/FixedPointConversion.swift -- %target-build-swift %t/FixedPointConversion.swift -o %t/a.out_Release -O
//
// RUN: %S/../../utils/line-directive %t/FixedPointConversion.swift -- %target-run %t/a.out_Debug
// RUN: %S/../../utils/line-directive %t/FixedPointConversion.swift -- %target-run %t/a.out_Release
// REQUIRES: executable_test

%{
import gyb
}%

import StdlibUnittest

var FixedPointConversionTraps = TestSuite("FixedPointToFixedPointConversionTraps")
var FixedPointConversionFailure = TestSuite("FixedPointToFixedPointConversionFailures")

%{

int_to_int_conversion_template = gyb.parse_template("int_to_int_conversion",
"""
%{
from SwiftIntTypes import all_integer_types

def intMax(bits, signed):
bits = bits - 1 if signed else bits
return (1 << bits) - 1

def intMin(bits, signed):
return -1 * intMax(bits, signed) - 1 if signed else 0

}%

% for self_ty in all_integer_types(word_bits):
% selfBits = self_ty.bits
% selfSigned = self_ty.is_signed
% selfMin = intMin(selfBits, selfSigned)
% selfMax = intMax(selfBits, selfSigned)
% Self = self_ty.stdlib_name

% for other_ty in all_integer_types(word_bits):
% otherBits = other_ty.bits
% otherSigned = other_ty.is_signed
% otherMin = intMin(otherBits, otherSigned)
% otherMax = intMax(otherBits, otherSigned)
% Other = other_ty.stdlib_name

% for testValue in [selfMin, selfMax, selfMin - 1, selfMax + 1, otherMin, otherMax]:

% if testValue < otherMin or testValue > otherMax:
% # Can't construct `other` value, do nothing and continue.

% elif testValue >= selfMin and testValue <= selfMax:

/// Always-safe conversion from ${Other}(${testValue}) to ${Self}.
FixedPointConversionTraps.test("${Other}To${Self}Conversion/dest=${testValue}") {
// Test that nothing interesting happens and we end up with the same result after converting.
let input = get${Other}(${testValue})
let result = ${Self}(input)
expectEqual(${testValue}, result)
_blackHole(result)
}

/// Never-nil failable conversion from ${Other}(${testValue}) to ${Self}.
FixedPointConversionFailure.test("${Other}To${Self}FailableConversion/dest=${testValue}") {
// Test that nothing interesting happens and we end up with a non-nil, identical result.
let input = get${Other}(${testValue})
var result = ${Self}(exactly: input)
expectNotEqual(result, nil)
expectEqual(${testValue}, result)
_blackHole(result)
}

% else:

/// Always-failing conversion from ${Other}(${testValue}) to ${Self}.
FixedPointConversionTraps.test("${Other}To${Self}Conversion/dest=${testValue}") {
// Test that we check if we fail and crash when an integer would be truncated in conversion.
let input = get${Other}(${testValue})
expectCrashLater()
var result = ${Self}(input)
_blackHole(result)
}

/// Always-nil failable conversion from ${Other}(${testValue}) to ${Self}.
FixedPointConversionFailure.test("${Other}To${Self}Conversion/dest=${testValue}") {
// Test that we check if we return nil when an integer would be truncated in conversion.
let input = get${Other}(${testValue})
var result = ${Self}(exactly: input)
expectEqual(nil, result)
_blackHole(result)
}
% end

% end # for testValue in ...
% end # for in all_integer_types (Other)
% end # for in all_integer_types (Self)
""")

}%

#if arch(i386) || arch(arm)

${gyb.execute_template(
int_to_int_conversion_template,
word_bits=32)}

#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x)

${gyb.execute_template(
int_to_int_conversion_template,
word_bits=64)}

#else

_UnimplementedError()

#endif

runAllTests()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a trailing newline?