[ClangImporter] Teach the importer about more infix operators between

integer constants, and to always look through macro definitions for them.

Also, logical comparisons now return a Boolean.

New operations: +, -, *, /, ^, >>, ==, >, >=, <, <=
This commit is contained in:
SpringsUp
2017-01-19 08:04:39 +01:00
parent 52fb930e04
commit fa834e2f80
5 changed files with 505 additions and 120 deletions

View File

@@ -7160,8 +7160,14 @@ ClangImporter::Implementation::createConstant(Identifier name, DeclContext *dc,
// Create the expression node.
StringRef printedValueCopy(context.AllocateCopy(printedValue));
if (value.getKind() == clang::APValue::Int) {
if (type->getCanonicalType()->isBool()) {
expr = new (context) BooleanLiteralExpr(value.getInt().getBoolValue(),
SourceLoc(),
/**Implicit=*/true);
} else {
expr = new (context) IntegerLiteralExpr(printedValueCopy, SourceLoc(),
/*Implicit=*/true);
}
} else {
expr = new (context) FloatLiteralExpr(printedValueCopy, SourceLoc(),
/*Implicit=*/true);

View File

@@ -22,6 +22,7 @@
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Stmt.h"
@@ -177,13 +178,6 @@ static bool isStringToken(const clang::Token &tok) {
tok.is(clang::tok::utf8_string_literal);
}
static bool isBinaryOperator(const clang::Token &tok) {
return tok.is(clang::tok::amp) ||
tok.is(clang::tok::pipe) ||
tok.is(clang::tok::ampamp) ||
tok.is(clang::tok::pipepipe);
}
// Describes the kind of string literal we're importing.
enum class MappedStringLiteralKind {
CString, // "string"
@@ -299,6 +293,47 @@ static Optional<clang::QualType> builtinTypeForToken(const clang::Token &tok,
}
}
static Optional<std::pair<llvm::APSInt, Type>>
getIntegerConstantForMacroToken(ClangImporter::Implementation &impl,
DeclContext *DC,
const clang::Token &token) {
// Integer literal.
if (token.is(clang::tok::numeric_constant)) {
if (auto literal = parseNumericLiteral<clang::IntegerLiteral>(impl,token)) {
auto value = llvm::APSInt { literal->getValue(),
literal->getType()->isUnsignedIntegerType() };
auto type = impl.importType(literal->getType(),
ImportTypeKind::Value,
isInSystemModule(DC),
/*isFullyBridgeable*/false);
return {{ value, type }};
}
// Macro identifier.
} else if (token.is(clang::tok::identifier) &&
token.getIdentifierInfo()->hasMacroDefinition()) {
auto rawID = token.getIdentifierInfo();
auto macroInfo = impl.getClangPreprocessor().getMacroInfo(rawID);
auto importedID = impl.getNameImporter().importMacroName(rawID, macroInfo);
impl.importMacro(importedID, macroInfo);
auto searcher = impl.ImportedMacroConstants.find(macroInfo);
if (searcher == impl.ImportedMacroConstants.end()) {
return None;
}
auto importedConstant = searcher->second;
if (!importedConstant.first.isInt()) {
return None;
}
return {{ importedConstant.first.getInt(), importedConstant.second }};
}
return None;
}
static ValueDecl *importMacro(ClangImporter::Implementation &impl,
DeclContext *DC,
Identifier name,
@@ -424,121 +459,152 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
if (first.is(clang::tok::at) && isStringToken(second))
return importStringLiteral(impl, DC, macro, name, second,
MappedStringLiteralKind::NSString, ClangN);
break;
}
case 3: {
// Check for a three-token expression of the form <number> << <number>.
// No signs or inner parentheses are allowed here.
// FIXME: What about people who define BIT_MASK(pos) helper macros?
if (tokenI[0].is(clang::tok::numeric_constant) &&
tokenI[1].is(clang::tok::lessless) &&
tokenI[2].is(clang::tok::numeric_constant)) {
auto *base = parseNumericLiteral<clang::IntegerLiteral>(impl, tokenI[0]);
auto *shift = parseNumericLiteral<clang::IntegerLiteral>(impl, tokenI[2]);
if (!base || !shift)
return nullptr;
// Check for infix operations between two integer constants.
// Import the result as another integer constant:
// #define INT3 (INT1 <op> INT2)
// Doesn't allow inner parentheses.
auto clangTy = base->getType();
auto type = impl.importType(clangTy, ImportTypeKind::Value,
isInSystemModule(DC),
/*isFullyBridgeable*/false);
if (!type)
return nullptr;
llvm::APSInt value{ base->getValue() << shift->getValue(),
clangTy->isUnsignedIntegerType() };
return createMacroConstant(impl, macro, name, DC, type,
clang::APValue(value),
ConstantConvertKind::Coerce, /*isStatic=*/false,
ClangN);
// Check for an expression of the form (FLAG1 | FLAG2), (FLAG1 & FLAG2),
// (FLAG1 || FLAG2), or (FLAG1 || FLAG2)
} else if (tokenI[0].is(clang::tok::identifier) &&
isBinaryOperator(tokenI[1]) &&
tokenI[2].is(clang::tok::identifier)) {
auto firstID = tokenI[0].getIdentifierInfo();
auto secondID = tokenI[2].getIdentifierInfo();
if (firstID->hasMacroDefinition() && secondID->hasMacroDefinition()) {
auto firstMacroInfo = impl.getClangPreprocessor().getMacroInfo(firstID);
auto secondMacroInfo = impl.getClangPreprocessor().getMacroInfo(
secondID);
auto firstIdentifier =
impl.getNameImporter().importMacroName(firstID, firstMacroInfo);
auto secondIdentifier =
impl.getNameImporter().importMacroName(secondID, secondMacroInfo);
impl.importMacro(firstIdentifier, firstMacroInfo);
impl.importMacro(secondIdentifier, secondMacroInfo);
auto firstIterator = impl.ImportedMacroConstants.find(firstMacroInfo);
if (firstIterator == impl.ImportedMacroConstants.end()) {
return nullptr;
}
auto secondIterator = impl.ImportedMacroConstants.find(secondMacroInfo);
if (secondIterator == impl.ImportedMacroConstants.end()) {
return nullptr;
}
auto firstConstant = firstIterator->second;
auto secondConstant = secondIterator->second;
auto firstValue = firstConstant.first;
auto secondValue = secondConstant.first;
if (!firstValue.isInt() || !secondValue.isInt()) {
return nullptr;
}
auto firstInteger = firstValue.getInt();
auto secondInteger = secondValue.getInt();
auto firstBitWidth = firstInteger.getBitWidth();
auto secondBitWidth = secondInteger.getBitWidth();
auto type = firstConstant.second;
clang::APValue value;
if (tokenI[1].is(clang::tok::pipe)) {
if (firstBitWidth < secondBitWidth) {
firstInteger = firstInteger.extend(secondBitWidth);
type = secondConstant.second;
} else if (secondBitWidth < firstBitWidth) {
secondInteger = secondInteger.extend(firstBitWidth);
type = firstConstant.second;
}
firstInteger.setIsUnsigned(true);
secondInteger.setIsUnsigned(true);
value = clang::APValue(firstInteger | secondInteger);
} else if (tokenI[1].is(clang::tok::amp)) {
if (firstBitWidth < secondBitWidth) {
firstInteger = firstInteger.extend(secondBitWidth);
type = secondConstant.second;
} else if (secondBitWidth < firstBitWidth) {
secondInteger = secondInteger.extend(firstBitWidth);
type = firstConstant.second;
}
firstInteger.setIsUnsigned(true);
secondInteger.setIsUnsigned(true);
value = clang::APValue(firstInteger & secondInteger);
} else if (tokenI[1].is(clang::tok::pipepipe)) {
auto firstBool = firstInteger.getBoolValue();
auto secondBool = firstInteger.getBoolValue();
auto result = firstBool || secondBool;
value = clang::APValue(result ?
llvm::APSInt::get(1) : llvm::APSInt::get(0));
} else if (tokenI[1].is(clang::tok::ampamp)) {
auto firstBool = firstInteger.getBoolValue();
auto secondBool = firstInteger.getBoolValue();
auto result = firstBool && secondBool;
value = clang::APValue(result ?
llvm::APSInt::get(1) : llvm::APSInt::get(0));
// Parse INT1.
llvm::APSInt firstValue;
Type firstSwiftType = nullptr;
if (auto firstInt = getIntegerConstantForMacroToken(impl, DC, tokenI[0])) {
firstValue = firstInt->first;
firstSwiftType = firstInt->second;
} else {
return nullptr;
}
return createMacroConstant(impl, macro, name, DC, type,
value,
// Parse INT2.
llvm::APSInt secondValue;
Type secondSwiftType = nullptr;
if (auto secondInt = getIntegerConstantForMacroToken(impl, DC, tokenI[2])) {
secondValue = secondInt->first;
secondSwiftType = secondInt->second;
} else {
return nullptr;
}
llvm::APSInt resultValue;
Type resultSwiftType = nullptr;
// Resolve width and signedness differences and find the type of the result.
auto firstIntSpec = clang::ento::APSIntType(firstValue);
auto secondIntSpec = clang::ento::APSIntType(secondValue);
if (firstIntSpec == std::max(firstIntSpec, secondIntSpec)) {
firstIntSpec.apply(secondValue);
resultSwiftType = firstSwiftType;
} else {
secondIntSpec.apply(firstValue);
resultSwiftType = secondSwiftType;
}
// Addition.
if (tokenI[1].is(clang::tok::plus)) {
resultValue = firstValue + secondValue;
// Subtraction.
} else if (tokenI[1].is(clang::tok::minus)) {
resultValue = firstValue - secondValue;
// Multiplication.
} else if (tokenI[1].is(clang::tok::star)) {
resultValue = firstValue * secondValue;
// Division.
} else if (tokenI[1].is(clang::tok::slash)) {
if (secondValue == 0) { return nullptr; }
resultValue = firstValue / secondValue;
// Left-shift.
} else if (tokenI[1].is(clang::tok::lessless)) {
// Shift by a negative number is UB in C. Don't import.
if (secondValue.isNegative()) { return nullptr; }
resultValue = llvm::APSInt { firstValue.shl(secondValue),
firstValue.isUnsigned() };
// Right-shift.
} else if (tokenI[1].is(clang::tok::greatergreater)) {
// Shift by a negative number is UB in C. Don't import.
if (secondValue.isNegative()) { return nullptr; }
if (firstValue.isUnsigned()) {
resultValue = llvm::APSInt { firstValue.lshr(secondValue),
/*isUnsigned*/ true };
} else {
resultValue = llvm::APSInt { firstValue.ashr(secondValue),
/*isUnsigned*/ false };
}
// Bitwise OR.
} else if (tokenI[1].is(clang::tok::pipe)) {
firstValue.setIsUnsigned(true);
secondValue.setIsUnsigned(true);
resultValue = llvm::APSInt { firstValue | secondValue,
/*isUnsigned*/ true };
// Bitwise AND.
} else if (tokenI[1].is(clang::tok::amp)) {
firstValue.setIsUnsigned(true);
secondValue.setIsUnsigned(true);
resultValue = llvm::APSInt { firstValue & secondValue,
/*isUnsigned*/ true };
// XOR.
} else if (tokenI[1].is(clang::tok::caret)) {
firstValue.setIsUnsigned(true);
secondValue.setIsUnsigned(true);
resultValue = llvm::APSInt { firstValue ^ secondValue,
/*isUnsigned*/ true };
// Logical OR.
} else if (tokenI[1].is(clang::tok::pipepipe)) {
bool result = firstValue.getBoolValue() || secondValue.getBoolValue();
resultValue = llvm::APSInt::get(result);
resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType();
// Logical AND.
} else if (tokenI[1].is(clang::tok::ampamp)) {
bool result = firstValue.getBoolValue() && secondValue.getBoolValue();
resultValue = llvm::APSInt::get(result);
resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType();
// Equality.
} else if (tokenI[1].is(clang::tok::equalequal)) {
resultValue = llvm::APSInt::get(firstValue == secondValue);
resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType();
// Less than.
} else if (tokenI[1].is(clang::tok::less)) {
resultValue = llvm::APSInt::get(firstValue < secondValue);
resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType();
// Less than or equal.
} else if (tokenI[1].is(clang::tok::lessequal)) {
resultValue = llvm::APSInt::get(firstValue <= secondValue);
resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType();
// Greater than.
} else if (tokenI[1].is(clang::tok::greater)) {
resultValue = llvm::APSInt::get(firstValue > secondValue);
resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType();
// Greater than or equal.
} else if (tokenI[1].is(clang::tok::greaterequal)) {
resultValue = llvm::APSInt::get(firstValue >= secondValue);
resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType();
// Unhandled operators.
} else {
return nullptr;
}
return createMacroConstant(impl, macro, name, DC, resultSwiftType,
clang::APValue(resultValue),
ConstantConvertKind::Coerce,
/*isStatic=*/false, ClangN);
}
}
break;
}
case 4: {
// Check for a CFString literal of the form CFSTR("string").
if (tokenI[0].is(clang::tok::identifier) &&
@@ -549,6 +615,7 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
return importStringLiteral(impl, DC, macro, name, tokenI[2],
MappedStringLiteralKind::CFString, ClangN);
}
// FIXME: Handle BIT_MASK(pos) helper macros which expand to a constant?
break;
}
case 5:

View File

@@ -0,0 +1,185 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil %s | %FileCheck %s
import macros
// CHECK-LABEL: // testBitwiseOperations() -> ()
func testBitwiseOperations() {
// CHECK: %0 = integer_literal $Builtin.Int64, -1, loc {{.*}}
// CHECK-NEXT: %1 = struct $UInt64 (%0 : $Builtin.Int64), loc {{.*}}
_ = DISPATCH_TIME_FOREVER as CUnsignedLongLong
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 1, loc {{.*}}
// CHECK-NEXT: %3 = struct $Int32 (%2 : $Builtin.Int32), loc {{.*}}
_ = BIT_SHIFT_1 as CInt
// CHECK-NEXT: %4 = integer_literal $Builtin.Int32, 4, loc {{.*}}
// CHECK-NEXT: %5 = struct $Int32 (%4 : $Builtin.Int32), loc {{.*}}
_ = BIT_SHIFT_2 as CInt
// CHECK-NEXT: %6 = integer_literal $Builtin.Int64, 24, loc {{.*}}
// CHECK-NEXT: %7 = struct $Int64 (%6 : $Builtin.Int64), loc {{.*}}
_ = BIT_SHIFT_3 as CLongLong
// CHECK-NEXT: %8 = integer_literal $Builtin.Int32, 2, loc {{.*}}
// CHECK-NEXT: %9 = struct $UInt32 (%8 : $Builtin.Int32), loc {{.*}}
_ = BIT_SHIFT_4 as CUnsignedInt
// CHECK-NEXT: %10 = integer_literal $Builtin.Int32, 1, loc {{.*}}
// CHECK-NEXT: %11 = struct $UInt32 (%10 : $Builtin.Int32), loc {{.*}}
_ = RSHIFT_ONE as CUnsignedInt
// CHECK-NEXT: %12 = integer_literal $Builtin.Int32, -2, loc {{.*}}
// CHECK-NEXT: %13 = struct $Int32 (%12 : $Builtin.Int32), loc {{.*}}
_ = RSHIFT_NEG as CInt
// CHECK-NEXT: %14 = integer_literal $Builtin.Int64, -4294967296, loc {{.*}}
// CHECK-NEXT: %15 = struct $UInt64 (%14 : $Builtin.Int64), loc {{.*}}
_ = XOR_HIGH as CUnsignedLongLong
// CHECK-NEXT: %16 = integer_literal $Builtin.Int32, 256, loc {{.*}}
// CHECK-NEXT: %17 = struct $Int32 (%16 : $Builtin.Int32), loc {{.*}}
_ = ATTR_BOLD as CInt
// CHECK-NEXT: %18 = integer_literal $Builtin.Int32, 512, loc {{.*}}
// CHECK-NEXT: %19 = struct $Int32 (%18 : $Builtin.Int32), loc {{.*}}
_ = ATTR_ITALIC as CInt
// CHECK-NEXT: %20 = integer_literal $Builtin.Int32, 1024, loc {{.*}}
// CHECK-NEXT: %21 = struct $Int32 (%20 : $Builtin.Int32), loc {{.*}}
_ = ATTR_UNDERLINE as CInt
}
// CHECK-LABEL: // testIntegerArithmetic() -> ()
func testIntegerArithmetic() {
// CHECK: %0 = integer_literal $Builtin.Int32, 0, loc {{.*}}
// CHECK-NEXT: %1 = struct $Int32 (%0 : $Builtin.Int32), loc {{.*}}
_ = ADD_ZERO as CInt
// CHECK-NEXT: %2 = integer_literal $Builtin.Int32, 1, loc {{.*}}
// CHECK-NEXT: %3 = struct $Int32 (%2 : $Builtin.Int32), loc {{.*}}
_ = ADD_ONE as CInt
// CHECK-NEXT: %4 = integer_literal $Builtin.Int32, 2, loc {{.*}}
// CHECK-NEXT: %5 = struct $Int32 (%4 : $Builtin.Int32), loc {{.*}}
_ = ADD_TWO as CInt
// CHECK-NEXT: %6 = integer_literal $Builtin.Int32, -2, loc {{.*}}
// CHECK-NEXT: %7 = struct $Int32 (%6 : $Builtin.Int32), loc {{.*}}
_ = ADD_MINUS_TWO as CInt
// CHECK-NEXT: %8 = integer_literal $Builtin.Int64, 169, loc {{.*}}
// CHECK-NEXT: %9 = struct $Int64 (%8 : $Builtin.Int64), loc {{.*}}
_ = ADD_MIXED_WIDTH as CLongLong
// CHECK-NEXT: %10 = integer_literal $Builtin.Int64, 142, loc {{.*}}
// CHECK-NEXT: %11 = struct $Int64 (%10 : $Builtin.Int64), loc {{.*}}
_ = ADD_MIXED_SIGN as CLongLong
// CHECK-NEXT: %12 = integer_literal $Builtin.Int32, -3, loc {{.*}}
// CHECK-NEXT: %13 = struct $UInt32 (%12 : $Builtin.Int32), loc {{.*}}
_ = ADD_UNDERFLOW as CUnsignedInt
// CHECK-NEXT: %14 = integer_literal $Builtin.Int32, 2, loc {{.*}}
// CHECK-NEXT: %15 = struct $UInt32 (%14 : $Builtin.Int32), loc {{.*}}
_ = ADD_OVERFLOW as CUnsignedInt
// CHECK-NEXT: %16 = integer_literal $Builtin.Int32, 1, loc {{.*}}
// CHECK-NEXT: %17 = struct $Int32 (%16 : $Builtin.Int32), loc {{.*}}
_ = SUB_ONE as CInt
// CHECK-NEXT: %18 = integer_literal $Builtin.Int32, 0, loc {{.*}}
// CHECK-NEXT: %19 = struct $Int32 (%18 : $Builtin.Int32), loc {{.*}}
_ = SUB_ZERO as CInt
// CHECK-NEXT: %20 = integer_literal $Builtin.Int32, -1, loc {{.*}}
// CHECK-NEXT: %21 = struct $Int32 (%20 : $Builtin.Int32), loc {{.*}}
_ = SUB_MINUS_ONE as CInt
// CHECK-NEXT: %22 = integer_literal $Builtin.Int64, 42, loc {{.*}}
// CHECK-NEXT: %23 = struct $Int64 (%22 : $Builtin.Int64), loc {{.*}}
_ = SUB_MIXED_WIDTH as CLongLong
// CHECK-NEXT: %24 = integer_literal $Builtin.Int32, 51, loc {{.*}}
// CHECK-NEXT: %25 = struct $UInt32 (%24 : $Builtin.Int32), loc {{.*}}
_ = SUB_MIXED_SIGN as CUnsignedInt
// CHECK-NEXT: %26 = integer_literal $Builtin.Int32, -1, loc {{.*}}
// CHECK-NEXT: %27 = struct $UInt32 (%26 : $Builtin.Int32), loc {{.*}}
_ = SUB_UNDERFLOW as CUnsignedInt
// CHECK-NEXT: %28 = integer_literal $Builtin.Int32, 1, loc {{.*}}
// CHECK-NEXT: %29 = struct $UInt32 (%28 : $Builtin.Int32), loc {{.*}}
_ = SUB_OVERFLOW as CUnsignedInt
// CHECK-NEXT: %30 = integer_literal $Builtin.Int32, 36, loc {{.*}}
// CHECK-NEXT: %31 = struct $Int32 (%30 : $Builtin.Int32), loc {{.*}}
_ = MULT_POS as CInt
// CHECK-NEXT: %32 = integer_literal $Builtin.Int32, -12, loc {{.*}}
// CHECK-NEXT: %33 = struct $Int32 (%32 : $Builtin.Int32), loc {{.*}}
_ = MULT_NEG as CInt
// CHECK-NEXT: %34 = integer_literal $Builtin.Int64, 8589934590, loc {{.*}}
// CHECK-NEXT: %35 = struct $Int64 (%34 : $Builtin.Int64), loc {{.*}}
_ = MULT_MIXED_TYPES as CLongLong
// CHECK-NEXT: %36 = integer_literal $Builtin.Int32, 128, loc {{.*}}
// CHECK-NEXT: %37 = struct $Int32 (%36 : $Builtin.Int32), loc {{.*}}
_ = DIVIDE_INTEGRAL as CInt
// CHECK-NEXT: %38 = integer_literal $Builtin.Int32, 1, loc {{.*}}
// CHECK-NEXT: %39 = struct $Int32 (%38 : $Builtin.Int32), loc {{.*}}
_ = DIVIDE_NONINTEGRAL as CInt
// CHECK-NEXT: %40 = integer_literal $Builtin.Int64, 2147483648, loc {{.*}}
// CHECK-NEXT: %41 = struct $Int64 (%40 : $Builtin.Int64), loc {{.*}}
_ = DIVIDE_MIXED_TYPES as CLongLong
}
// CHECK-LABEL: // testIntegerComparisons() -> ()
func testIntegerComparisons() {
// CHECK: %0 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %1 = struct $Bool (%0 : $Builtin.Int1), loc {{.*}}
_ = EQUAL_FALSE
// CHECK-NEXT: %2 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %3 = struct $Bool (%2 : $Builtin.Int1), loc {{.*}}
_ = EQUAL_TRUE
// CHECK-NEXT: %4 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %5 = struct $Bool (%4 : $Builtin.Int1), loc {{.*}}
_ = EQUAL_TRUE_MIXED_TYPES
// CHECK-NEXT: %6 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %7 = struct $Bool (%6 : $Builtin.Int1), loc {{.*}}
_ = GT_FALSE
// CHECK-NEXT: %8 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %9 = struct $Bool (%8 : $Builtin.Int1), loc {{.*}}
_ = GT_TRUE
// CHECK-NEXT: %10 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %11 = struct $Bool (%10 : $Builtin.Int1), loc {{.*}}
_ = GTE_FALSE
// CHECK-NEXT: %12 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %13 = struct $Bool (%12 : $Builtin.Int1), loc {{.*}}
_ = GTE_TRUE
// CHECK-NEXT: %14 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %15 = struct $Bool (%14 : $Builtin.Int1), loc {{.*}}
_ = LT_FALSE
// CHECK-NEXT: %16 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %17 = struct $Bool (%16 : $Builtin.Int1), loc {{.*}}
_ = LT_TRUE
// CHECK-NEXT: %18 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %19 = struct $Bool (%18 : $Builtin.Int1), loc {{.*}}
_ = LTE_FALSE
// CHECK-NEXT: %20 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %21 = struct $Bool (%20 : $Builtin.Int1), loc {{.*}}
_ = LTE_TRUE
}
// CHECK-LABEL: // testLogicalComparisons() -> ()
func testLogicalComparisons() {
// CHECK: %0 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %1 = struct $Bool (%0 : $Builtin.Int1), loc {{.*}}
_ = L_AND_TRUE
// CHECK-NEXT: %2 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %3 = struct $Bool (%2 : $Builtin.Int1), loc {{.*}}
_ = L_AND_FALSE
// CHECK-NEXT: %4 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %5 = struct $Bool (%4 : $Builtin.Int1), loc {{.*}}
_ = L_AND_TRUE_B
// CHECK-NEXT: %6 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %7 = struct $Bool (%6 : $Builtin.Int1), loc {{.*}}
_ = L_AND_FALSE_B
// CHECK-NEXT: %8 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %9 = struct $Bool (%8 : $Builtin.Int1), loc {{.*}}
_ = L_OR_TRUE
// CHECK-NEXT: %10 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %11 = struct $Bool (%10 : $Builtin.Int1), loc {{.*}}
_ = L_OR_FALSE
// CHECK-NEXT: %12 = integer_literal $Builtin.Int1, -1, loc {{.*}}
// CHECK-NEXT: %13 = struct $Bool (%12 : $Builtin.Int1), loc {{.*}}
_ = L_OR_TRUE_B
// CHECK-NEXT: %14 = integer_literal $Builtin.Int1, 0, loc {{.*}}
// CHECK-NEXT: %15 = struct $Bool (%14 : $Builtin.Int1), loc {{.*}}
_ = L_OR_FALSE_B
}

View File

@@ -109,6 +109,60 @@ func testBitwiseOps() {
_ = (BIT_SHIFT_1 | BIT_SHIFT_2) as CInt
_ = BIT_SHIFT_3 as CLongLong
_ = BIT_SHIFT_4 as CUnsignedInt
_ = RSHIFT_ONE as CUnsignedInt
_ = RSHIFT_INVALID // expected-error {{use of unresolved identifier 'RSHIFT_INVALID'}}
_ = XOR_HIGH as CUnsignedLongLong
var attributes = 0 as CInt
attributes |= ATTR_BOLD
attributes |= ATTR_ITALIC
attributes |= ATTR_UNDERLINE
attributes |= ATTR_INVALID // expected-error {{use of unresolved identifier 'ATTR_INVALID'}}
}
func testIntegerArithmetic() {
_ = ADD_ZERO as CInt
_ = ADD_ONE as CInt
_ = ADD_TWO as CInt
_ = ADD_MINUS_TWO as CInt
_ = ADD_MIXED_WIDTH as CLongLong
_ = ADD_MIXED_SIGN as CLongLong
_ = ADD_UNDERFLOW as CUnsignedInt
_ = ADD_OVERFLOW as CUnsignedInt
_ = SUB_ONE as CInt
_ = SUB_ZERO as CInt
_ = SUB_MINUS_ONE as CInt
_ = SUB_MIXED_WIDTH as CLongLong
_ = SUB_MIXED_SIGN as CUnsignedInt
_ = SUB_UNDERFLOW as CUnsignedInt
_ = SUB_OVERFLOW as CUnsignedInt
_ = MULT_POS as CInt
_ = MULT_NEG as CInt
_ = MULT_MIXED_TYPES as CLongLong
_ = DIVIDE_INTEGRAL as CInt
_ = DIVIDE_NONINTEGRAL as CInt
_ = DIVIDE_MIXED_TYPES as CLongLong
_ = DIVIDE_INVALID // expected-error {{use of unresolved identifier 'DIVIDE_INVALID'}}
}
func testIntegerComparisons() {
if EQUAL_FALSE, EQUAL_TRUE, EQUAL_TRUE_MIXED_TYPES,
GT_FALSE, GT_TRUE, GTE_FALSE, GTE_TRUE,
LT_FALSE, LT_TRUE, LTE_FALSE, LTE_TRUE {
fatalError("You hit the jackpot!")
}
}
func testLogicalComparisons() {
if L_AND_TRUE, L_AND_FALSE, L_AND_TRUE_B, L_AND_FALSE_B,
L_OR_TRUE, L_OR_FALSE, L_OR_TRUE_B, L_OR_FALSE_B {
fatalError("Yet again!")
}
}
func testRecursion() {

View File

@@ -12,6 +12,7 @@
#define EOF (-1)
#define UINT32_MAX 0xFFFFFFFFU
#define INT64_MAX 0x7FFFFFFFFFFFFFFFLL
#define UINT64_MAX 0xFFFFFFFFFFFFFFFFLL
#define MINUS_THREE -3
#define true 1
#define false 0
@@ -50,14 +51,86 @@
#endif
#define NULL_AS_CLASS_NIL Nil
#define RECURSION RECURSION
#define REF_TO_RECURSION RECURSION
#define DISPATCH_TIME_NOW (0ull)
#define DISPATCH_TIME_FOREVER (~0ull)
// Bitwise Operations.
#define BIT_SHIFT_1 (1 << 0)
#define BIT_SHIFT_2 (1 << 2)
#define BIT_SHIFT_3 (3LL << 3)
#define BIT_SHIFT_4 (1U << 1)
#define RECURSION RECURSION
#define REF_TO_RECURSION RECURSION
#define STARTPOS_ATTRS 8
#define ATTR_BOLD (1 << STARTPOS_ATTRS)
#define ATTR_ITALIC (2 << STARTPOS_ATTRS)
#define ATTR_UNDERLINE (4 << STARTPOS_ATTRS)
#define ATTR_INVALID (4 << MINUS_THREE) // Should skip. Negative shift.
#define RSHIFT_ONE (UINT32_MAX >> 31)
#define RSHIFT_NEG (MINUS_THREE >> 1)
#define RSHIFT_INVALID (0xFFFF >> MINUS_THREE) // Should skip. Negative shift.
#define XOR_HIGH (UINT64_MAX ^ UINT32_MAX)
// Integer Arithmetic.
#define ADD_ZERO (0 + 0)
#define ADD_ONE (ADD_ZERO + 1)
#define ADD_TWO (ADD_ONE + ADD_ONE)
#define ADD_MINUS_TWO (1 + MINUS_THREE)
#define ADD_MIXED_WIDTH (3 + 166LL) // Result = 169LL.
#define ADD_MIXED_SIGN (42U + 100LL) // Result = 142LL.
#define ADD_UNDERFLOW (0U + MINUS_THREE) // Result = (UINT32_MAX - 2)U.
#define ADD_OVERFLOW (UINT32_MAX + 3) // Result = 2U.
#define SUB_ONE (ADD_TWO - 1)
#define SUB_ZERO (SUB_ONE - SUB_ONE)
#define SUB_MINUS_ONE (SUB_ZERO - SUB_ONE)
#define SUB_MIXED_WIDTH (64 - 22LL) // Result = 42LL.
#define SUB_MIXED_SIGN (100U - 49) // Result = 51U.
#define SUB_UNDERFLOW (0U - 1) // Result = (UINT32_MAX)U.
#define SUB_OVERFLOW (UINT32_MAX - ADD_MINUS_TWO) // Result = 1U.
#define MULT_POS (12 * 3)
#define MULT_NEG (4 * MINUS_THREE)
#define MULT_MIXED_TYPES (2LL * UINT32_MAX) // Result = (2^33)LL
#define DIVIDE_INTEGRAL (1024 / 8)
#define DIVIDE_NONINTEGRAL (3 / 2)
#define DIVIDE_MIXED_TYPES (INT64_MAX / UINT32_MAX) // Result = (2^31)LL
#define DIVIDE_INVALID (69 / 0) // Should skip. Divide by zero.
// Integer Comparisons.
#define EQUAL_FALSE (99 == 66)
#define EQUAL_TRUE (SUB_ONE == 1)
#define EQUAL_TRUE_MIXED_TYPES (UINT32_MAX == 4294967295LL)
#define GT_FALSE (SUB_ONE > 50)
#define GT_TRUE (INT64_MAX > UINT32_MAX)
#define MINUS_FIFTY (0 - 50)
#define GTE_FALSE (MINUS_FIFTY >= 50)
#define GTE_TRUE (INT64_MAX >= UINT32_MAX)
#define LT_FALSE (SUB_UNDERFLOW < UINT32_MAX)
#define MINUS_NINETY_NINE (-99)
#define MINUS_FORTY_TWO (-42)
#define LT_TRUE (MINUS_NINETY_NINE < MINUS_FORTY_TWO)
#define LTE_FALSE (ADD_ONE <= 0)
#define LTE_TRUE (SUB_UNDERFLOW <= UINT32_MAX)
// Logical Comparisons
#define L_AND_TRUE (1 && 1)
#define L_AND_FALSE (0 && 1)
#define L_AND_TRUE_B (EQUAL_TRUE && EQUAL_TRUE_MIXED_TYPES)
#define L_AND_FALSE_B (EQUAL_FALSE && EQUAL_TRUE)
#define L_OR_TRUE (0 || 1)
#define L_OR_FALSE (0 || 0)
#define L_OR_TRUE_B (EQUAL_FALSE || EQUAL_TRUE)
#define L_OR_FALSE_B (EQUAL_FALSE || L_OR_FALSE)