mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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) {
|
||||
expr = new (context) IntegerLiteralExpr(printedValueCopy, SourceLoc(),
|
||||
/*Implicit=*/true);
|
||||
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);
|
||||
|
||||
@@ -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,120 +459,151 @@ 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));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
return createMacroConstant(impl, macro, name, DC, type,
|
||||
value,
|
||||
ConstantConvertKind::Coerce,
|
||||
/*isStatic=*/false, ClangN);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
break;
|
||||
|
||||
// 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);
|
||||
}
|
||||
case 4: {
|
||||
// Check for a CFString literal of the form CFSTR("string").
|
||||
@@ -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:
|
||||
|
||||
185
test/ClangImporter/macro_literals.swift
Normal file
185
test/ClangImporter/macro_literals.swift
Normal 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
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user