Skip to content

Commit f00c578

Browse files
authored
Merge pull request swiftlang#34401 from xedin/implicit-cgfloat-conversion
[DNM][TypeChecker] Implement Double <-> CGFloat implicit conversion
2 parents 21fb139 + a559ca8 commit f00c578

23 files changed

+691
-37
lines changed

include/swift/AST/Types.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,14 @@ class alignas(1 << TypeAlignInBits) TypeBase {
800800

801801
/// Check if this is a nominal type defined at the top level of the Swift module
802802
bool isStdlibType();
803-
803+
804+
/// Check if this is a CGFloat type from CoreGraphics framework
805+
/// on macOS or Foundation on Linux.
806+
bool isCGFloatType();
807+
808+
/// Check if this is a Double type from standard library.
809+
bool isDoubleType();
810+
804811
/// Check if this is either an Array, Set or Dictionary collection type defined
805812
/// at the top level of the Swift module
806813
bool isKnownStdlibCollectionType();

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,9 @@ namespace swift {
592592

593593
/// See \ref FrontendOptions.PrintFullConvention
594594
bool PrintFullConvention = false;
595+
596+
/// Disallow Double and CGFloat types from being used interchangeably.
597+
bool DisableImplicitDoubleCGFloatConversion = false;
595598
};
596599

597600
/// Options for controlling the behavior of the Clang importer.

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ def enable_resilience : Flag<["-"], "enable-resilience">,
248248
// HIDDEN FLAGS
249249
let Flags = [FrontendOption, NoDriverOption, HelpHidden] in {
250250

251+
def disable_implicit_double_cgfloat_conversion : Flag<["-"], "disable-implicit-double-cgfloat-conversion">,
252+
HelpText<"Disable implicit conversion between Double and CGFloat types">;
253+
251254
def debug_constraints : Flag<["-"], "debug-constraints">,
252255
HelpText<"Debug the constraint-based type checker">;
253256

include/swift/Sema/Constraint.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ enum class ConversionRestrictionKind {
261261
/// Implicit conversion from an Objective-C class type to its
262262
/// toll-free-bridged CF type.
263263
ObjCTollFreeBridgeToCF,
264+
/// Implicit conversion from a value of Double to a value of CGFloat type via
265+
/// an implicit CGFloat initializer call.
266+
DoubleToCGFloat,
267+
/// Implicit conversion from a value of CGFloat type to a value of Double type
268+
/// via an implicit Double initializer call passing a CGFloat value.
269+
CGFloatToDouble,
264270
};
265271

266272
/// Specifies whether a given conversion requires the creation of a temporary

include/swift/Sema/ConstraintLocator.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class ProtocolConformance;
4444
namespace constraints {
4545

4646
class ConstraintSystem;
47+
enum class ConversionRestrictionKind;
4748

4849
/// Locates a given constraint within the expression being
4950
/// type-checked, which may refer down into subexpressions and parts of
@@ -805,6 +806,26 @@ class LocatorPathElt::PlaceholderType final
805806
}
806807
};
807808

809+
class LocatorPathElt::ImplicitConversion final
810+
: public StoredIntegerElement<1> {
811+
public:
812+
ImplicitConversion(ConversionRestrictionKind kind)
813+
: StoredIntegerElement(ConstraintLocator::ImplicitConversion,
814+
static_cast<unsigned>(kind)) {}
815+
816+
ConversionRestrictionKind getConversionKind() const {
817+
return static_cast<ConversionRestrictionKind>(getValue());
818+
}
819+
820+
bool is(ConversionRestrictionKind kind) const {
821+
return getConversionKind() == kind;
822+
}
823+
824+
static bool classof(const LocatorPathElt *elt) {
825+
return elt->getKind() == ConstraintLocator::ImplicitConversion;
826+
}
827+
};
828+
808829
namespace details {
809830
template <typename CustomPathElement>
810831
class PathElement {

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ CUSTOM_LOCATOR_PATH_ELT(ConformanceRequirement)
201201
/// A source-specified placeholder.
202202
CUSTOM_LOCATOR_PATH_ELT(PlaceholderType)
203203

204+
/// The implicit conversion applied at a given location.
205+
CUSTOM_LOCATOR_PATH_ELT(ImplicitConversion)
206+
204207
#undef LOCATOR_PATH_ELT
205208
#undef CUSTOM_LOCATOR_PATH_ELT
206209
#undef SIMPLE_LOCATOR_PATH_ELT

include/swift/Sema/ConstraintSystem.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -553,8 +553,12 @@ template <typename T = Expr> T *castToExpr(ASTNode node) {
553553
}
554554

555555
template <typename T = Expr> T *getAsExpr(ASTNode node) {
556+
if (node.isNull())
557+
return nullptr;
558+
556559
if (auto *E = node.dyn_cast<Expr *>())
557560
return dyn_cast_or_null<T>(E);
561+
558562
return nullptr;
559563
}
560564

@@ -788,6 +792,10 @@ enum ScoreKind {
788792
SK_UnresolvedMemberViaOptional,
789793
/// An implicit force of an implicitly unwrapped optional value.
790794
SK_ForceUnchecked,
795+
/// An implicit conversion from a value of one type (lhs)
796+
/// to another type (rhs) via implicit initialization of
797+
/// `rhs` type with an argument of `lhs` value.
798+
SK_ImplicitValueConversion,
791799
/// A user-defined conversion.
792800
SK_UserConversion,
793801
/// A non-trivial function conversion.
@@ -1169,6 +1177,10 @@ class Solution {
11691177
/// The locators of \c Defaultable constraints whose defaults were used.
11701178
llvm::SmallPtrSet<ConstraintLocator *, 2> DefaultedConstraints;
11711179

1180+
/// Implicit value conversions applied for a given locator.
1181+
SmallVector<std::pair<ConstraintLocator *, ConversionRestrictionKind>, 2>
1182+
ImplicitValueConversions;
1183+
11721184
/// The node -> type mappings introduced by this solution.
11731185
llvm::DenseMap<ASTNode, Type> nodeTypes;
11741186

@@ -1393,6 +1405,10 @@ enum class ConstraintSystemFlags {
13931405
/// calling conventions, say due to Clang attributes such as
13941406
/// `__attribute__((ns_consumed))`.
13951407
UseClangFunctionTypes = 0x80,
1408+
1409+
/// Disallow using Double and CGFloat interchangeably by means of
1410+
/// an implicit value conversion.
1411+
DisableImplicitDoubleCGFloatConversion = 0x100,
13961412
};
13971413

13981414
/// Options that affect the constraint system as a whole.
@@ -2202,6 +2218,11 @@ class ConstraintSystem {
22022218
std::vector<std::pair<ConstraintLocator*, TrailingClosureMatching>>
22032219
trailingClosureMatchingChoices;
22042220

2221+
/// The set of implicit value conversions performed by the solver on
2222+
/// a current path to reach a solution.
2223+
SmallVector<std::pair<ConstraintLocator *, ConversionRestrictionKind>, 2>
2224+
ImplicitValueConversions;
2225+
22052226
/// The worklist of "active" constraints that should be revisited
22062227
/// due to a change.
22072228
ConstraintList ActiveConstraints;
@@ -2730,6 +2751,9 @@ class ConstraintSystem {
27302751
/// The length of \c caseLabelItems.
27312752
unsigned numCaseLabelItems;
27322753

2754+
/// The length of \c ImplicitValueConversions.
2755+
unsigned numImplicitValueConversions;
2756+
27332757
/// The previous score.
27342758
Score PreviousScore;
27352759

@@ -3996,7 +4020,7 @@ class ConstraintSystem {
39964020
/// \returns \c true if an error was encountered, \c false otherwise.
39974021
bool simplifyAppliedOverloadsImpl(Constraint *disjunction,
39984022
TypeVariableType *fnTypeVar,
3999-
const FunctionType *argFnType,
4023+
FunctionType *argFnType,
40004024
unsigned numOptionalUnwraps,
40014025
ConstraintLocatorBuilder locator);
40024026

@@ -4021,7 +4045,7 @@ class ConstraintSystem {
40214045
/// call.
40224046
///
40234047
/// \returns \c true if an error was encountered, \c false otherwise.
4024-
bool simplifyAppliedOverloads(Type fnType, const FunctionType *argFnType,
4048+
bool simplifyAppliedOverloads(Type fnType, FunctionType *argFnType,
40254049
ConstraintLocatorBuilder locator);
40264050

40274051
/// Retrieve the type that will be used when matching the given overload.
@@ -5334,11 +5358,12 @@ class DisjunctionChoice {
53345358

53355359
bool isBeginningOfPartition() const { return IsBeginningOfPartition; }
53365360

5337-
// FIXME: Both of the accessors below are required to support
5361+
// FIXME: All three of the accessors below are required to support
53385362
// performance optimization hacks in constraint solver.
53395363

53405364
bool isGenericOperator() const;
53415365
bool isSymmetricOperator() const;
5366+
bool isUnaryOperator() const;
53425367

53435368
void print(llvm::raw_ostream &Out, SourceManager *SM) const {
53445369
Out << "disjunction choice ";

lib/AST/Type.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,28 @@ bool TypeBase::isStdlibType() {
813813
return false;
814814
}
815815

816+
bool TypeBase::isCGFloatType() {
817+
auto *NTD = getAnyNominal();
818+
if (!NTD)
819+
return false;
820+
821+
auto *DC = NTD->getDeclContext();
822+
if (!DC->isModuleScopeContext())
823+
return false;
824+
825+
auto *module = DC->getParentModule();
826+
// On macOS `CGFloat` is part of a `CoreGraphics` module,
827+
// but on Linux it could be found in `Foundation`.
828+
return (module->getName().is("CoreGraphics") ||
829+
module->getName().is("Foundation")) &&
830+
NTD->getName().is("CGFloat");
831+
}
832+
833+
bool TypeBase::isDoubleType() {
834+
auto *NTD = getAnyNominal();
835+
return NTD ? NTD->getDecl() == getASTContext().getDoubleDecl() : false;
836+
}
837+
816838
bool TypeBase::isKnownStdlibCollectionType() {
817839
if (auto *structType = getAs<BoundGenericStructType>()) {
818840
auto &ctx = getASTContext();

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,9 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args,
830830
Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints);
831831
Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures);
832832

833+
Opts.DisableImplicitDoubleCGFloatConversion |=
834+
Args.hasArg(OPT_disable_implicit_double_cgfloat_conversion);
835+
833836
for (const Arg *A : Args.filtered(OPT_debug_constraints_on_line)) {
834837
unsigned line;
835838
if (StringRef(A->getValue()).getAsInteger(/*radix*/ 10, line)) {

lib/Sema/CSApply.cpp

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5743,7 +5743,7 @@ Expr *ExprRewriter::coerceCallArguments(
57435743
// wrapped value placeholder, the first non-defaulted argument must be
57445744
// wrapped in an OpaqueValueExpr.
57455745
bool shouldInjectWrappedValuePlaceholder =
5746-
target->shouldInjectWrappedValuePlaceholder(apply);
5746+
target && target->shouldInjectWrappedValuePlaceholder(apply);
57475747

57485748
auto injectWrappedValuePlaceholder =
57495749
[&](Expr *arg, bool isAutoClosure = false) -> Expr * {
@@ -6749,6 +6749,59 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
67496749
return cs.cacheType(new (ctx)
67506750
ForeignObjectConversionExpr(result, toType));
67516751
}
6752+
6753+
case ConversionRestrictionKind::CGFloatToDouble:
6754+
case ConversionRestrictionKind::DoubleToCGFloat: {
6755+
auto conversionKind = knownRestriction->second;
6756+
6757+
auto *argExpr = locator.trySimplifyToExpr();
6758+
assert(argExpr);
6759+
6760+
// Load the value for conversion.
6761+
argExpr = cs.coerceToRValue(argExpr);
6762+
6763+
auto *implicitInit =
6764+
CallExpr::createImplicit(ctx, TypeExpr::createImplicit(toType, ctx),
6765+
/*args=*/{argExpr},
6766+
/*argLabels=*/{Identifier()});
6767+
6768+
cs.cacheExprTypes(implicitInit->getFn());
6769+
cs.setType(argExpr, fromType);
6770+
cs.setType(implicitInit->getArg(),
6771+
ParenType::get(cs.getASTContext(), fromType));
6772+
6773+
auto *callLocator = cs.getConstraintLocator(
6774+
implicitInit, LocatorPathElt::ImplicitConversion(conversionKind));
6775+
6776+
// HACK: Temporarily push the call expr onto the expr stack to make sure
6777+
// we don't try to prematurely close an existential when applying the
6778+
// curried member ref. This can be removed once existential opening is
6779+
// refactored not to rely on the shape of the AST prior to rewriting.
6780+
ExprStack.push_back(implicitInit);
6781+
SWIFT_DEFER { ExprStack.pop_back(); };
6782+
6783+
// We need to take information recorded for all conversions of this
6784+
// kind and move it to a specific location where restriction is applied.
6785+
{
6786+
auto *memberLoc = solution.getConstraintLocator(
6787+
callLocator, {ConstraintLocator::ApplyFunction,
6788+
ConstraintLocator::ConstructorMember});
6789+
6790+
auto overload = solution.getOverloadChoice(cs.getConstraintLocator(
6791+
ASTNode(), {LocatorPathElt::ImplicitConversion(conversionKind),
6792+
ConstraintLocator::ApplyFunction,
6793+
ConstraintLocator::ConstructorMember}));
6794+
6795+
solution.overloadChoices.insert({memberLoc, overload});
6796+
solution.trailingClosureMatchingChoices.insert(
6797+
{cs.getConstraintLocator(callLocator,
6798+
ConstraintLocator::ApplyArgument),
6799+
TrailingClosureMatching::Forward});
6800+
}
6801+
6802+
finishApply(implicitInit, toType, callLocator, callLocator);
6803+
return implicitInit;
6804+
}
67526805
}
67536806
}
67546807

@@ -8588,16 +8641,15 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
85888641
// If we're supposed to convert the expression to some particular type,
85898642
// do so now.
85908643
if (shouldCoerceToContextualType()) {
8591-
resultExpr = Rewriter.coerceToType(resultExpr,
8592-
solution.simplifyType(convertType),
8593-
cs.getConstraintLocator(expr));
8644+
resultExpr =
8645+
Rewriter.coerceToType(resultExpr, solution.simplifyType(convertType),
8646+
cs.getConstraintLocator(resultExpr));
85948647
} else if (cs.getType(resultExpr)->hasLValueType() &&
85958648
!target.isDiscardedExpr()) {
85968649
// We referenced an lvalue. Load it.
85978650
resultExpr = Rewriter.coerceToType(
8598-
resultExpr,
8599-
cs.getType(resultExpr)->getRValueType(),
8600-
cs.getConstraintLocator(expr));
8651+
resultExpr, cs.getType(resultExpr)->getRValueType(),
8652+
cs.getConstraintLocator(resultExpr));
86018653
}
86028654

86038655
if (!resultExpr)

0 commit comments

Comments
 (0)