Skip to content

[Clang importer] swift_newtype with bridged computed rawValue #2319

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 2 commits into from
Apr 27, 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
201 changes: 170 additions & 31 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,30 @@ static Pattern *createTypedNamedPattern(VarDecl *decl) {
return P;
}

/// Create a var member for this struct, along with its pattern binding, and add
/// it as a member
static std::pair<VarDecl *, PatternBindingDecl *>
createVarWithPattern(ASTContext &cxt, DeclContext *dc, Identifier name, Type ty,
bool isLet, bool isImplicit,
Accessibility setterAccessibility) {
// Create a variable to store the underlying value.
auto var = new (cxt) VarDecl(
/*static*/ false,
/*IsLet*/ isLet, SourceLoc(), name, ty, dc);
if (isImplicit)
var->setImplicit();
var->setAccessibility(Accessibility::Public);
var->setSetterAccessibility(setterAccessibility);

// Create a pattern binding to describe the variable.
Pattern *varPattern = createTypedNamedPattern(var);
auto patternBinding =
PatternBindingDecl::create(cxt, SourceLoc(), StaticSpellingKind::None,
SourceLoc(), varPattern, nullptr, dc);

return {var, patternBinding};
}

#ifndef NDEBUG
static bool verifyNameMapping(MappedTypeNameKind NameMapping,
StringRef left, StringRef right) {
Expand Down Expand Up @@ -1286,11 +1310,6 @@ namespace {
// For now, fall through and treat as a struct
case clang::SwiftNewtypeAttr::NK_Struct: {

auto underlyingType = Impl.importType(
Decl->getUnderlyingType(), ImportTypeKind::Typedef,
isInSystemModule(DC),
Decl->getUnderlyingType()->isBlockPointerType());

auto &cxt = Impl.SwiftContext;
auto Loc = Impl.importSourceLoc(Decl->getLocation());

Expand All @@ -1301,9 +1320,33 @@ namespace {
ProtocolDecl *protocols[] = {
cxt.getProtocol(KnownProtocolKind::RawRepresentable),
};
makeStructRawValued(structDecl, underlyingType,
{KnownProtocolKind::RawRepresentable},
protocols);

// Import the type of the underlying storage
auto storedUnderlyingType = Impl.importType(
Decl->getUnderlyingType(), ImportTypeKind::Value,
isInSystemModule(DC),
Decl->getUnderlyingType()->isBlockPointerType());

// Find a bridged type, which may be different
auto computedPropertyUnderlyingType = Impl.importType(
Decl->getUnderlyingType(), ImportTypeKind::Property,
isInSystemModule(DC),
Decl->getUnderlyingType()->isBlockPointerType());

if (storedUnderlyingType.getCanonicalTypeOrNull() ==
computedPropertyUnderlyingType.getCanonicalTypeOrNull()) {
// Simple, our stored type is already bridged
makeStructRawValued(structDecl, storedUnderlyingType,
{KnownProtocolKind::RawRepresentable},
protocols);
} else {
// We need to make a stored rawValue or storage type, and a
// computed one of bridged type.
makeStructRawValuedWithBridge(
structDecl, storedUnderlyingType,
computedPropertyUnderlyingType,
{KnownProtocolKind::RawRepresentable}, protocols);
}

Impl.ImportedDecls[Decl->getCanonicalDecl()] = structDecl;
Impl.registerExternalDecl(structDecl);
Expand Down Expand Up @@ -1445,28 +1488,15 @@ namespace {
bool makeUnlabeledValueInit = false,
bool isImplicit = true) {
auto &cxt = Impl.SwiftContext;
populateInheritedTypes(structDecl, protocols);

// Note synthesized protocols
for (auto kind : synthesizedProtocolAttrs)
structDecl->getAttrs().add(new (cxt) SynthesizedProtocolAttr(kind));
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs, protocols);

// Create a variable to store the underlying value.
auto varName = cxt.Id_rawValue;
auto var = new (cxt) VarDecl(
/*static*/ false,
/*IsLet*/ isLet, SourceLoc(), varName, underlyingType, structDecl);
if (isImplicit)
var->setImplicit();
var->setAccessibility(Accessibility::Public);
var->setSetterAccessibility(setterAccessibility);
VarDecl *var;
PatternBindingDecl *patternBinding;
std::tie(var, patternBinding) =
createVarWithPattern(cxt, structDecl, cxt.Id_rawValue, underlyingType,
isLet, isImplicit, setterAccessibility);

// Create a pattern binding to describe the variable.
Pattern *varPattern = createTypedNamedPattern(var);

auto patternBinding = PatternBindingDecl::create(
cxt, SourceLoc(), StaticSpellingKind::None, SourceLoc(), varPattern,
nullptr, structDecl);
structDecl->setHasDelayedMembers();

// Create constructors to initialize that value from a value of the
Expand All @@ -1480,11 +1510,101 @@ namespace {
createValueConstructor(structDecl, var,
/*wantCtorParamNames=*/true,
/*wantBody=*/!Impl.hasFinishedTypeChecking()));

structDecl->addMember(patternBinding);
structDecl->addMember(var);
}

/// Make a struct declaration into a raw-value-backed struct, with
/// bridged computed rawValue property which differs from stored backing
///
/// \param structDecl the struct to make a raw value for
/// \param storedUnderlyingType the type of the stored raw value
/// \param bridgedType the type of the 'rawValue' computed property bridge
/// \param synthesizedProtocolAttrs synthesized protocol attributes to add
/// \param protocols the protocols to make this struct conform to
///
/// This will perform most of the work involved in making a new Swift struct
/// be backed by a stored raw value and computed raw value of bridged type.
/// This will populated derived protocols and synthesized protocols, add the
/// new variable and pattern bindings, and create the inits parameterized
/// over a bridged type that will cast to the stored type, as appropriate.
///
template <unsigned N> void makeStructRawValuedWithBridge(
StructDecl *structDecl,
Type storedUnderlyingType,
Type bridgedType,
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
ProtocolDecl *const(&protocols)[N]) {
auto &cxt = Impl.SwiftContext;
addProtocolsToStruct(structDecl, synthesizedProtocolAttrs,
protocols);

auto storedVarName = cxt.getIdentifier("_rawValue");
auto computedVarName = cxt.Id_rawValue;

// Create a variable to store the underlying value.
VarDecl *storedVar;
PatternBindingDecl *storedPatternBinding;
std::tie(storedVar, storedPatternBinding) = createVarWithPattern(
cxt, structDecl, storedVarName, storedUnderlyingType, /*isLet=*/false,
/*isImplicit=*/true, Accessibility::Private);

//
// Create a computed value variable
auto dre = new (cxt) DeclRefExpr(
storedVar, {}, true, AccessSemantics::Ordinary, storedUnderlyingType);
auto coerce = new (cxt)
CoerceExpr(dre, {}, {nullptr, bridgedType});
auto computedVar = cast<VarDecl>(Impl.createConstant(
computedVarName, structDecl, bridgedType, coerce,
ConstantConvertKind::Coerce, false, {}));
computedVar->setImplicit();
computedVar->setAccessibility(Accessibility::Public);

// Create a pattern binding to describe the variable.

Pattern *computedVarPattern = createTypedNamedPattern(computedVar);
auto computedPatternBinding = PatternBindingDecl::create(
cxt, SourceLoc(), StaticSpellingKind::None, SourceLoc(),
computedVarPattern, nullptr, structDecl);

auto init = createValueConstructor(structDecl, computedVar,
/*wantCtorParamNames=*/true,
/*wantBody=*/false);
// Insert our custom init body
if (!Impl.hasFinishedTypeChecking()) {
auto selfDecl = ParamDecl::createSelf(SourceLoc(), structDecl,
/*static*/ false, /*inout*/ true);
// Construct left-hand side.
Expr *lhs = new (cxt) DeclRefExpr(selfDecl, DeclNameLoc(),
/*Implicit=*/true);
lhs = new (cxt) MemberRefExpr(lhs, SourceLoc(), storedVar,
DeclNameLoc(), /*Implicit=*/true);

// Construct right-hand side.
// FIXME: get the parameter from the init, and plug it in here.
auto rhs = new (cxt)
DeclRefExpr(init->getParameterList(1)->get(0), DeclNameLoc(),
/*Implicit=*/true);

// Add assignment.
auto assign = new (cxt) AssignExpr(lhs, SourceLoc(), rhs,
/*Implicit=*/true);
auto body = BraceStmt::create(cxt, SourceLoc(), {assign}, SourceLoc());
init->setBody(body);

// We want to inline away as much of this as we can
init->getAttrs().add(new (cxt) TransparentAttr(/*implicit*/ true));
}

structDecl->setHasDelayedMembers();
structDecl->addMember(init);
structDecl->addMember(storedPatternBinding);
structDecl->addMember(storedVar);
structDecl->addMember(computedPatternBinding);
structDecl->addMember(computedVar);
}

/// \brief Create a constructor that initializes a struct from its members.
ConstructorDecl *createValueConstructor(StructDecl *structDecl,
ArrayRef<VarDecl *> members,
Expand Down Expand Up @@ -1711,6 +1831,20 @@ namespace {
nominal->setCheckedInheritanceClause();
}

/// Add protocol conformances and synthesized protocol attributes
template <unsigned N>
void
addProtocolsToStruct(StructDecl *structDecl,
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
ProtocolDecl *const(&protocols)[N]) {
populateInheritedTypes(structDecl, protocols);

// Note synthesized protocols
for (auto kind : synthesizedProtocolAttrs)
structDecl->getAttrs().add(new (Impl.SwiftContext)
SynthesizedProtocolAttr(kind));
}

NominalTypeDecl *importAsOptionSetType(DeclContext *dc,
Identifier name,
const clang::EnumDecl *decl) {
Expand Down Expand Up @@ -6200,9 +6334,14 @@ ClangImporter::Implementation::createConstant(Identifier name, DeclContext *dc,
ClangNode ClangN) {
auto &context = SwiftContext;

auto var = createDeclWithClangNode<VarDecl>(ClangN,
isStatic, /*IsLet*/ false,
SourceLoc(), name, type, dc);
VarDecl *var = nullptr;
if (ClangN) {
var = createDeclWithClangNode<VarDecl>(ClangN, isStatic, /*IsLet*/ false,
SourceLoc(), name, type, dc);
} else {
var = new (SwiftContext)
VarDecl(isStatic, /*IsLet*/ false, SourceLoc(), name, type, dc);
}

// Form the argument patterns.
SmallVector<ParameterList*, 3> getterArgs;
Expand Down
10 changes: 8 additions & 2 deletions test/IDE/Inputs/custom-modules/Newtype.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@import Foundation;

typedef NSString *SNTErrorDomain __attribute((swift_newtype(struct)))
typedef NSString *__nonnull SNTErrorDomain __attribute((swift_newtype(struct)))
__attribute((swift_name("ErrorDomain")));

extern void SNTErrorDomainProcess(SNTErrorDomain d)
Expand All @@ -18,9 +18,15 @@ extern const SNTErrorDomain SNTFive
extern const SNTErrorDomain SNTElsewhere
__attribute((swift_name("Foo.err")));

typedef NSString *SNTClosedEnum __attribute((swift_newtype(enum)))
typedef NSString *__nullable SNTClosedEnum __attribute((swift_newtype(enum)))
__attribute((swift_name("ClosedEnum")));

extern const SNTClosedEnum SNTFirstClosedEntryEnum;
extern const SNTClosedEnum SNTSecondEntry;
extern const SNTClosedEnum SNTClosedEnumThirdEntry;

typedef NSString * IUONewtype __attribute((swift_newtype(struct)));

typedef float MyFloat __attribute((swift_newtype(struct)));
extern const MyFloat globalFloat;

28 changes: 20 additions & 8 deletions test/IDE/newtype.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
// REQUIRES: objc_interop

// PRINT-LABEL: struct ErrorDomain : RawRepresentable {
// PRINT-NEXT: init(rawValue: NSString)
// PRINT-NEXT: let rawValue: NSString
// PRINT-NEXT: init(rawValue: String)
// PRINT-NEXT: var _rawValue: NSString
// PRINT-NEXT: var rawValue: String { get }
// PRINT-NEXT: }
// PRINT-NEXT: extension ErrorDomain {
// PRINT-NEXT: func process()
Expand All @@ -20,17 +21,28 @@
// PRINT-NEXT: extension Foo {
// PRINT-NEXT: static let err: ErrorDomain
// PRINT-NEXT: }
//
// TODO: update test when we can import it as actual new enum
// PRINT-LABEL: struct ClosedEnum : RawRepresentable {
// PRINT-NEXT: init(rawValue: NSString)
// PRINT-NEXT: let rawValue: NSString
// PRINT-NEXT: struct ClosedEnum : RawRepresentable {
// PRINT-NEXT: init(rawValue: String?)
// PRINT-NEXT: var _rawValue: NSString?
// PRINT-NEXT: var rawValue: String? { get }
// PRINT-NEXT: }
// PRINT-NEXT: extension ClosedEnum {
// PRINT-NEXT: static let firstClosedEntryEnum: ClosedEnum
// PRINT-NEXT: static let secondEntry: ClosedEnum
// PRINT-NEXT: static let thirdEntry: ClosedEnum
// PRINT-NEXT: }
// PRINT-NEXT: struct IUONewtype : RawRepresentable {
// PRINT-NEXT: init(rawValue: String!)
// PRINT-NEXT: var _rawValue: NSString!
// PRINT-NEXT: var rawValue: String! { get }
// PRINT-NEXT: }
// PRINT-NEXT: struct MyFloat : RawRepresentable {
// PRINT-NEXT: init(rawValue: Float)
// PRINT-NEXT: let rawValue: Float
// PRINT-NEXT: }
// PRINT-NEXT: extension MyFloat {
// PRINT-NEXT: static let globalFloat: MyFloat
// PRINT-NEXT: }

// RUN: %target-parse-verify-swift -I %S/Inputs/custom-modules
import Newtype
Expand All @@ -47,7 +59,7 @@ func tests() {
thirdEnum.process()
// expected-error@-1{{value of type 'ClosedEnum' has no member 'process'}}

let _ = ErrorDomain(rawValue: thirdEnum.rawValue)
let _ = ErrorDomain(rawValue: thirdEnum.rawValue!)
let _ = ClosedEnum(rawValue: errOne.rawValue)

}