mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
ClangImporter support for bitfields
Bitfields are imported as computed properties with Clang-generated accessors. While we cannot represent them directly in SIL, we can still synthesize a memberwise initializer, so also decouple that notion from "has unreferenceable storage". Fixes <rdar://problem/21702107>. Swift SVN r31779
This commit is contained in:
@@ -792,14 +792,19 @@ static FuncDecl *makeFieldSetterDecl(ClangImporter::Implementation &Impl,
|
||||
static std::pair<FuncDecl *, FuncDecl *>
|
||||
makeUnionFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
StructDecl *importedUnionDecl,
|
||||
VarDecl *fieldDecl) {
|
||||
VarDecl *importedFieldDecl) {
|
||||
auto &C = Impl.SwiftContext;
|
||||
|
||||
auto getterDecl = makeFieldGetterDecl(Impl, importedUnionDecl, fieldDecl);
|
||||
auto setterDecl = makeFieldSetterDecl(Impl, importedUnionDecl, fieldDecl);
|
||||
auto getterDecl = makeFieldGetterDecl(Impl,
|
||||
importedUnionDecl,
|
||||
importedFieldDecl);
|
||||
|
||||
fieldDecl->makeComputed(SourceLoc(), getterDecl, setterDecl, nullptr,
|
||||
SourceLoc());
|
||||
auto setterDecl = makeFieldSetterDecl(Impl,
|
||||
importedUnionDecl,
|
||||
importedFieldDecl);
|
||||
|
||||
importedFieldDecl->makeComputed(SourceLoc(), getterDecl, setterDecl, nullptr,
|
||||
SourceLoc());
|
||||
|
||||
// Don't bother synthesizing the body if we've already finished type-checking.
|
||||
if (Impl.hasFinishedTypeChecking())
|
||||
@@ -860,6 +865,203 @@ makeUnionFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
return { getterDecl, setterDecl };
|
||||
}
|
||||
|
||||
static clang::DeclarationName
|
||||
getAccessorDeclarationName(clang::ASTContext &Ctx,
|
||||
clang::RecordDecl *structDecl,
|
||||
clang::FieldDecl *fieldDecl,
|
||||
const char *suffix) {
|
||||
std::string id;
|
||||
llvm::raw_string_ostream IdStream(id);
|
||||
IdStream << "$" << structDecl->getName()
|
||||
<< "$" << fieldDecl->getName()
|
||||
<< "$" << suffix;
|
||||
|
||||
return clang::DeclarationName(&Ctx.Idents.get(IdStream.str()));
|
||||
}
|
||||
|
||||
/// Build the bitfield getter and setter using Clang.
|
||||
///
|
||||
/// \code
|
||||
/// static inline int get(RecordType self) {
|
||||
/// return self.field;
|
||||
/// }
|
||||
/// static inline void set(int newValue, RecordType *self) {
|
||||
/// self->field = newValue;
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
/// \returns a pair of the getter and setter function decls.
|
||||
static std::pair<FuncDecl *, FuncDecl *>
|
||||
makeBitFieldAccessors(ClangImporter::Implementation &Impl,
|
||||
clang::RecordDecl *structDecl,
|
||||
StructDecl *importedStructDecl,
|
||||
clang::FieldDecl *fieldDecl,
|
||||
VarDecl *importedFieldDecl) {
|
||||
clang::ASTContext &Ctx = Impl.getClangASTContext();
|
||||
|
||||
// Getter: static inline FieldType get(RecordType self);
|
||||
auto recordType = Ctx.getRecordType(structDecl);
|
||||
auto recordPointerType = Ctx.getPointerType(recordType);
|
||||
auto fieldType = fieldDecl->getType();
|
||||
auto fieldNameInfo = clang::DeclarationNameInfo(fieldDecl->getDeclName(),
|
||||
clang::SourceLocation());
|
||||
|
||||
auto cGetterName = getAccessorDeclarationName(Ctx, structDecl, fieldDecl,
|
||||
"getter");
|
||||
auto cGetterType = Ctx.getFunctionType(fieldDecl->getType(),
|
||||
recordType,
|
||||
clang::FunctionProtoType::ExtProtoInfo());
|
||||
auto cGetterTypeInfo = Ctx.getTrivialTypeSourceInfo(cGetterType);
|
||||
auto cGetterDecl = clang::FunctionDecl::Create(Ctx,
|
||||
structDecl->getDeclContext(),
|
||||
clang::SourceLocation(),
|
||||
clang::SourceLocation(),
|
||||
cGetterName,
|
||||
cGetterType,
|
||||
cGetterTypeInfo,
|
||||
clang::SC_Static);
|
||||
cGetterDecl->setImplicitlyInline();
|
||||
assert(!cGetterDecl->isExternallyVisible());
|
||||
|
||||
auto getterDecl = makeFieldGetterDecl(Impl,
|
||||
importedStructDecl,
|
||||
importedFieldDecl,
|
||||
cGetterDecl);
|
||||
|
||||
// Setter: static inline void set(FieldType newValue, RecordType *self);
|
||||
SmallVector<clang::QualType, 8> cSetterParamTypes;
|
||||
cSetterParamTypes.push_back(fieldType);
|
||||
cSetterParamTypes.push_back(recordPointerType);
|
||||
|
||||
auto cSetterName = getAccessorDeclarationName(Ctx, structDecl, fieldDecl,
|
||||
"setter");
|
||||
auto cSetterType = Ctx.getFunctionType(Ctx.VoidTy,
|
||||
cSetterParamTypes,
|
||||
clang::FunctionProtoType::ExtProtoInfo());
|
||||
auto cSetterTypeInfo = Ctx.getTrivialTypeSourceInfo(cSetterType);
|
||||
|
||||
auto cSetterDecl = clang::FunctionDecl::Create(Ctx,
|
||||
structDecl->getDeclContext(),
|
||||
clang::SourceLocation(),
|
||||
clang::SourceLocation(),
|
||||
cSetterName,
|
||||
cSetterType,
|
||||
cSetterTypeInfo,
|
||||
clang::SC_Static);
|
||||
cSetterDecl->setImplicitlyInline();
|
||||
assert(!cSetterDecl->isExternallyVisible());
|
||||
|
||||
auto setterDecl = makeFieldSetterDecl(Impl,
|
||||
importedStructDecl,
|
||||
importedFieldDecl,
|
||||
cSetterDecl);
|
||||
|
||||
importedFieldDecl->makeComputed(SourceLoc(),
|
||||
getterDecl,
|
||||
setterDecl,
|
||||
nullptr,
|
||||
SourceLoc());
|
||||
|
||||
// Don't bother synthesizing the body if we've already finished type-checking.
|
||||
if (Impl.hasFinishedTypeChecking())
|
||||
return { getterDecl, setterDecl };
|
||||
|
||||
// Synthesize the getter body
|
||||
{
|
||||
auto cGetterSelfId = nullptr;
|
||||
auto recordTypeInfo = Ctx.getTrivialTypeSourceInfo(recordType);
|
||||
auto cGetterSelf = clang::ParmVarDecl::Create(Ctx, cGetterDecl,
|
||||
clang::SourceLocation(),
|
||||
clang::SourceLocation(),
|
||||
cGetterSelfId,
|
||||
recordType,
|
||||
recordTypeInfo,
|
||||
clang::SC_None,
|
||||
nullptr);
|
||||
cGetterDecl->setParams(cGetterSelf);
|
||||
|
||||
auto cGetterSelfExpr = new (Ctx) clang::DeclRefExpr(cGetterSelf, false,
|
||||
recordType,
|
||||
clang::VK_RValue,
|
||||
clang::SourceLocation());
|
||||
auto cGetterExpr = new (Ctx) clang::MemberExpr(cGetterSelfExpr,
|
||||
/*isarrow=*/ false,
|
||||
clang::SourceLocation(),
|
||||
fieldDecl,
|
||||
fieldNameInfo,
|
||||
fieldType,
|
||||
clang::VK_RValue,
|
||||
clang::OK_BitField);
|
||||
|
||||
auto cGetterBody = new (Ctx) clang::ReturnStmt(clang::SourceLocation(),
|
||||
cGetterExpr,
|
||||
nullptr);
|
||||
cGetterDecl->setBody(cGetterBody);
|
||||
|
||||
Impl.registerExternalDecl(getterDecl);
|
||||
}
|
||||
|
||||
// Synthesize the setter body
|
||||
{
|
||||
SmallVector<clang::ParmVarDecl *, 2> cSetterParams;
|
||||
auto fieldTypeInfo = Ctx.getTrivialTypeSourceInfo(fieldType);
|
||||
auto cSetterValue = clang::ParmVarDecl::Create(Ctx, cSetterDecl,
|
||||
clang::SourceLocation(),
|
||||
clang::SourceLocation(),
|
||||
/* nameID? */ nullptr,
|
||||
fieldType,
|
||||
fieldTypeInfo,
|
||||
clang::SC_None,
|
||||
nullptr);
|
||||
cSetterParams.push_back(cSetterValue);
|
||||
auto recordPointerTypeInfo = Ctx.getTrivialTypeSourceInfo(recordPointerType);
|
||||
auto cSetterSelf = clang::ParmVarDecl::Create(Ctx, cSetterDecl,
|
||||
clang::SourceLocation(),
|
||||
clang::SourceLocation(),
|
||||
/* nameID? */ nullptr,
|
||||
recordPointerType,
|
||||
recordPointerTypeInfo,
|
||||
clang::SC_None,
|
||||
nullptr);
|
||||
cSetterParams.push_back(cSetterSelf);
|
||||
cSetterDecl->setParams(cSetterParams);
|
||||
|
||||
auto cSetterSelfExpr = new (Ctx) clang::DeclRefExpr(cSetterSelf, false,
|
||||
recordPointerType,
|
||||
clang::VK_RValue,
|
||||
clang::SourceLocation());
|
||||
|
||||
auto cSetterMemberExpr = new (Ctx) clang::MemberExpr(cSetterSelfExpr,
|
||||
/*isarrow=*/ true,
|
||||
clang::SourceLocation(),
|
||||
fieldDecl,
|
||||
fieldNameInfo,
|
||||
fieldType,
|
||||
clang::VK_LValue,
|
||||
clang::OK_BitField);
|
||||
|
||||
auto cSetterValueExpr = new (Ctx) clang::DeclRefExpr(cSetterValue, false,
|
||||
fieldType,
|
||||
clang::VK_RValue,
|
||||
clang::SourceLocation());
|
||||
|
||||
auto cSetterExpr = new (Ctx) clang::BinaryOperator(cSetterMemberExpr,
|
||||
cSetterValueExpr,
|
||||
clang::BO_Assign,
|
||||
fieldType,
|
||||
clang::VK_RValue,
|
||||
clang::OK_Ordinary,
|
||||
clang::SourceLocation(),
|
||||
/*fpContractable=*/ false);
|
||||
|
||||
cSetterDecl->setBody(cSetterExpr);
|
||||
|
||||
Impl.registerExternalDecl(setterDecl);
|
||||
}
|
||||
|
||||
return { getterDecl, setterDecl };
|
||||
}
|
||||
|
||||
namespace {
|
||||
class CFPointeeInfo {
|
||||
bool IsValid;
|
||||
@@ -1530,22 +1732,28 @@ namespace {
|
||||
if (wantBody) {
|
||||
// Assign all of the member variables appropriately.
|
||||
SmallVector<ASTNode, 4> stmts;
|
||||
unsigned paramIdx = 0;
|
||||
for (auto var : members) {
|
||||
// Construct left-hand side.
|
||||
Expr *lhs = new (context) DeclRefExpr(selfDecl, SourceLoc(),
|
||||
/*Implicit=*/true);
|
||||
lhs = new (context) MemberRefExpr(lhs, SourceLoc(), var, SourceLoc(),
|
||||
/*Implicit=*/true);
|
||||
|
||||
// Construct right-hand side.
|
||||
auto param = params[paramIdx++];
|
||||
auto rhs = new (context) DeclRefExpr(param, SourceLoc(),
|
||||
/*Implicit=*/true);
|
||||
// To keep DI happy, initialize stored properties before computed.
|
||||
for (unsigned pass = 0; pass < 2; pass++) {
|
||||
for (unsigned i = 0, e = members.size(); i < e; i++) {
|
||||
auto var = members[i];
|
||||
if (var->hasStorage() == (pass != 0))
|
||||
continue;
|
||||
|
||||
// Add assignment.
|
||||
stmts.push_back(new (context) AssignExpr(lhs, SourceLoc(), rhs,
|
||||
/*Implicit=*/true));
|
||||
// Construct left-hand side.
|
||||
Expr *lhs = new (context) DeclRefExpr(selfDecl, SourceLoc(),
|
||||
/*Implicit=*/true);
|
||||
lhs = new (context) MemberRefExpr(lhs, SourceLoc(), var, SourceLoc(),
|
||||
/*Implicit=*/true);
|
||||
|
||||
// Construct right-hand side.
|
||||
auto rhs = new (context) DeclRefExpr(params[i], SourceLoc(),
|
||||
/*Implicit=*/true);
|
||||
|
||||
// Add assignment.
|
||||
stmts.push_back(new (context) AssignExpr(lhs, SourceLoc(), rhs,
|
||||
/*Implicit=*/true));
|
||||
}
|
||||
}
|
||||
|
||||
// Create the function body.
|
||||
@@ -2094,16 +2302,25 @@ namespace {
|
||||
|
||||
Decl *VisitRecordDecl(const clang::RecordDecl *decl) {
|
||||
// Track whether this record contains fields we can't reference in Swift
|
||||
// yet.
|
||||
// as stored properties.
|
||||
bool hasUnreferenceableStorage = false;
|
||||
|
||||
// Track whether this record contains fields that can't be zero-
|
||||
// initialized.
|
||||
bool hasZeroInitializableStorage = true;
|
||||
|
||||
if (decl->isUnion())
|
||||
// Track whether all fields in this record can be referenced in Swift,
|
||||
// either as stored or computed properties, in which case the record type
|
||||
// gets a memberwise initializer.
|
||||
bool hasMemberwiseInitializer = true;
|
||||
|
||||
if (decl->isUnion()) {
|
||||
hasUnreferenceableStorage = true;
|
||||
|
||||
// We generate initializers specially for unions below.
|
||||
hasMemberwiseInitializer = false;
|
||||
}
|
||||
|
||||
// FIXME: Skip Microsoft __interfaces.
|
||||
if (decl->isInterface())
|
||||
return nullptr;
|
||||
@@ -2134,14 +2351,6 @@ namespace {
|
||||
if (!dc)
|
||||
return nullptr;
|
||||
|
||||
for (auto m = decl->decls_begin(), mEnd = decl->decls_end();
|
||||
m != mEnd; ++m) {
|
||||
if (auto FD = dyn_cast<clang::FieldDecl>(*m))
|
||||
if (FD->isBitField())
|
||||
// We don't make bitfields accessible in Swift yet.
|
||||
hasUnreferenceableStorage = true;
|
||||
}
|
||||
|
||||
// Create the struct declaration and record it.
|
||||
auto result = Impl.createDeclWithClangNode<StructDecl>(decl,
|
||||
Impl.importSourceLoc(decl->getLocStart()),
|
||||
@@ -2168,6 +2377,7 @@ namespace {
|
||||
if (!nd) {
|
||||
// We couldn't import the member, so we can't reference it in Swift.
|
||||
hasUnreferenceableStorage = true;
|
||||
hasMemberwiseInitializer = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2176,17 +2386,24 @@ namespace {
|
||||
if (auto field = dyn_cast<clang::FieldDecl>(nd)) {
|
||||
if (field->isAnonymousStructOrUnion())
|
||||
continue;
|
||||
|
||||
// Non-nullable pointers can't be zero-initialized.
|
||||
if (auto nullability = field->getType()
|
||||
->getNullability(Impl.getClangASTContext())) {
|
||||
if (*nullability == clang::NullabilityKind::NonNull)
|
||||
hasZeroInitializableStorage = false;
|
||||
}
|
||||
|
||||
// TODO: If we had the notion of a closed enum with no private
|
||||
// cases or resilience concerns, then complete NS_ENUMs with
|
||||
// no case corresponding to zero would also not be zero-
|
||||
// initializable.
|
||||
|
||||
// Unnamed bitfields are just for padding and should not
|
||||
// inhibit creation of a memberwise initializer.
|
||||
if (field->isUnnamedBitfield()) {
|
||||
hasUnreferenceableStorage = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto member = Impl.importDecl(nd);
|
||||
@@ -2200,11 +2417,31 @@ namespace {
|
||||
// Otherwise, we don't know what this field is. Assume it may be
|
||||
// important in C.
|
||||
hasUnreferenceableStorage = true;
|
||||
hasMemberwiseInitializer = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto VD = cast<VarDecl>(member);
|
||||
|
||||
// Bitfields are imported as computed properties with Clang-generated
|
||||
// accessors.
|
||||
if (auto field = dyn_cast<clang::FieldDecl>(nd)) {
|
||||
if (field->isUnnamedBitfield())
|
||||
continue;
|
||||
|
||||
if (field->isBitField()) {
|
||||
// We can't represent this struct completely in SIL anymore,
|
||||
// but we're still able to define a memberwise initializer.
|
||||
hasUnreferenceableStorage = true;
|
||||
|
||||
makeBitFieldAccessors(Impl,
|
||||
const_cast<clang::RecordDecl *>(decl),
|
||||
result,
|
||||
const_cast<clang::FieldDecl *>(field),
|
||||
VD);
|
||||
}
|
||||
}
|
||||
|
||||
if (decl->isUnion()) {
|
||||
// Union fields should only be available indirectly via a computed
|
||||
// property. Since the union is made of all of the fields at once,
|
||||
@@ -2215,9 +2452,13 @@ namespace {
|
||||
if (isa<clang::IndirectFieldDecl>(nd))
|
||||
continue;
|
||||
|
||||
auto field = cast<clang::FieldDecl>(nd);
|
||||
|
||||
VD->setLet(false);
|
||||
Decl *getter, *setter;
|
||||
std::tie(getter, setter) = makeUnionFieldAccessors(Impl, result, VD);
|
||||
std::tie(getter, setter) = makeUnionFieldAccessors(Impl,
|
||||
result,
|
||||
VD);
|
||||
members.push_back(VD);
|
||||
|
||||
// Create labeled inititializers for unions that take one of the
|
||||
@@ -2238,15 +2479,21 @@ namespace {
|
||||
if (hasZeroInitializableStorage) {
|
||||
// Add constructors for the struct.
|
||||
ctors.push_back(createDefaultConstructor(result));
|
||||
if (!decl->isUnion() && hasReferenceableFields && !hasUnreferenceableStorage) {
|
||||
if (hasReferenceableFields && hasMemberwiseInitializer) {
|
||||
// The default zero initializer suppresses the implicit value
|
||||
// constructor that would normally be formed, so we have to add that
|
||||
// explicitly as well. We leave the body implicit in order to match
|
||||
// the behavior of the implicit constructor native structs receive.
|
||||
// explicitly as well.
|
||||
//
|
||||
// If we can completely represent the struct in SIL, leave the body
|
||||
// implicit, otherwise synthesize one to call property setters.
|
||||
bool wantBody = (hasUnreferenceableStorage &&
|
||||
!Impl.hasFinishedTypeChecking());
|
||||
auto valueCtor = createValueConstructor(result, members,
|
||||
/*want param names*/true,
|
||||
/*want body*/false);
|
||||
valueCtor->setIsMemberwiseInitializer();
|
||||
/*want body*/wantBody);
|
||||
if (!hasUnreferenceableStorage)
|
||||
valueCtor->setIsMemberwiseInitializer();
|
||||
|
||||
ctors.push_back(valueCtor);
|
||||
}
|
||||
}
|
||||
@@ -2480,11 +2727,6 @@ namespace {
|
||||
}
|
||||
|
||||
Decl *VisitFieldDecl(const clang::FieldDecl *decl) {
|
||||
// We don't import bitfields because we can not layout them correctly in
|
||||
// IRGen.
|
||||
if (decl->isBitField())
|
||||
return nullptr;
|
||||
|
||||
// Fields are imported as variables.
|
||||
auto name = Impl.importName(decl);
|
||||
if (name.empty())
|
||||
|
||||
@@ -1121,6 +1121,7 @@ namespace {
|
||||
const clang::ASTRecordLayout &layout,
|
||||
Address structAddr) {
|
||||
auto fieldIndex = field->getFieldIndex();
|
||||
assert(!field->isBitField());
|
||||
auto fieldOffset = Size(layout.getFieldOffset(fieldIndex) / 8);
|
||||
asImpl().visit(Ctx.getCanonicalType(field->getType()),
|
||||
createGEPAtOffset(structAddr, fieldOffset));
|
||||
|
||||
@@ -615,7 +615,9 @@ private:
|
||||
/// Place the next struct field at its appropriate offset.
|
||||
void addStructField(const clang::FieldDecl *clangField,
|
||||
VarDecl *swiftField) {
|
||||
Size offset(ClangLayout.getFieldOffset(clangField->getFieldIndex()) / 8);
|
||||
unsigned fieldOffset = ClangLayout.getFieldOffset(clangField->getFieldIndex());
|
||||
assert(!clangField->isBitField());
|
||||
Size offset(fieldOffset / 8);
|
||||
|
||||
// If we have a Swift import of this type, use our lowered information.
|
||||
if (swiftField) {
|
||||
|
||||
@@ -2,25 +2,22 @@
|
||||
|
||||
import ctypes
|
||||
|
||||
func testBitfieldMembers() -> StructWithBitfields {
|
||||
return StructWithBitfields()
|
||||
// TODO: Expose the bitfields as properties.
|
||||
}
|
||||
|
||||
func useStructWithBitfields(mrm: ModRM) -> ModRM {
|
||||
// TODO: Make bitfield fields available
|
||||
let rm: CUnsignedInt = mrm.rm // expected-error{{}}
|
||||
let reg: CUnsignedInt = mrm.reg // expected-error{{}}
|
||||
let mod: CUnsignedInt = mrm.mod // expected-error{{}}
|
||||
let rm: CUnsignedInt = mrm.rm
|
||||
let reg: CUnsignedInt = mrm.reg
|
||||
let mod: CUnsignedInt = mrm.mod
|
||||
let opcode: CUnsignedInt = mrm.opcode
|
||||
|
||||
var new: ModRM = ModRM()
|
||||
new.rm = rm
|
||||
new.reg = reg
|
||||
new.mod = mod
|
||||
new.opcode = opcode
|
||||
|
||||
return mrm
|
||||
}
|
||||
|
||||
// Incompletely imported structs shouldn't have elementwise initializers.
|
||||
// They can still be zero-initialized using the default initializer.
|
||||
func constructStructWithBitfields(x: CUnsignedInt) {
|
||||
_ = StructWithBitfields() as StructWithBitfields
|
||||
_ = StructWithBitfields(First: x) as StructWithBitfields// expected-error{{}}
|
||||
// TODO: Fully import bitfields.
|
||||
_ = StructWithBitfields(First: x, Second: x, Third: x) as StructWithBitfields// expected-error{{}}
|
||||
_ = StructWithBitfields()
|
||||
_ = StructWithBitfields(First: x, Second: x, Third: x)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
typedef struct ModRM {
|
||||
unsigned rm: 3;
|
||||
unsigned reg: 3;
|
||||
unsigned mod: 2;
|
||||
unsigned opcode;
|
||||
} ModRM;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module ctypes {
|
||||
header "unions.h"
|
||||
header "bitfields.h"
|
||||
export *
|
||||
}
|
||||
|
||||
34
test/Interpreter/c_bitfields.swift
Normal file
34
test/Interpreter/c_bitfields.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
// RUN: rm -rf %t && mkdir %t
|
||||
// RUN: %target-build-swift -I %S/../Inputs/clang-importer-sdk/platform/any/usr/include %s -o %t/a.out
|
||||
// RUN: %target-run %t/a.out
|
||||
// REQUIRES: executable_test
|
||||
|
||||
import StdlibUnittest
|
||||
|
||||
import ctypes
|
||||
|
||||
var BitfieldTestSuite = TestSuite("Bitfields")
|
||||
|
||||
BitfieldTestSuite.test("Simple") {
|
||||
var new: ModRM = ModRM()
|
||||
new.rm = 7
|
||||
new.reg = 5
|
||||
new.mod = 3
|
||||
new.opcode = 44
|
||||
|
||||
expectEqual(7, new.rm)
|
||||
expectEqual(5, new.reg)
|
||||
expectEqual(3, new.mod)
|
||||
expectEqual(44, new.opcode)
|
||||
}
|
||||
|
||||
BitfieldTestSuite.test("Initializer") {
|
||||
let new = ModRM(rm: 6, reg: 4, mod: 2, opcode: 33)
|
||||
|
||||
expectEqual(6, new.rm)
|
||||
expectEqual(4, new.reg)
|
||||
expectEqual(2, new.mod)
|
||||
expectEqual(33, new.opcode)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
Reference in New Issue
Block a user