-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Automatic conversion of mutable pointers to immutable pointers. #37214
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
Changes from all commits
79e9ded
c87f7ae
b3ea383
29446b3
58f57d2
4544f5e
bce05e9
8c18cc7
0487ea0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3635,6 +3635,10 @@ static bool repairOutOfOrderArgumentsInBinaryFunction( | |
return false; | ||
} | ||
|
||
/// Switch between restrictions mechanism and original PR using | ||
/// ConversionRestrictionKind::PointerToPointer in repairFailures(). | ||
static bool useRestrictions = true; | ||
|
||
/// Attempt to repair typing failures and record fixes if needed. | ||
/// \return true if at least some of the failures has been repaired | ||
/// successfully, which allows type matcher to continue. | ||
|
@@ -4971,9 +4975,90 @@ bool ConstraintSystem::repairFailures( | |
break; | ||
} | ||
|
||
// Accept mutable pointers in the place of immutables. | ||
if (implicitConversionAvailable(lhs, rhs)) | ||
conversionsOrFixes.push_back( | ||
ConversionRestrictionKind::ImplicitConversion); | ||
|
||
return !conversionsOrFixes.empty(); | ||
} | ||
|
||
ConstructorDecl *ConstraintSystem::implicitConversionAvailable(Type fromType, Type toType) { | ||
#if 01 // Determine implicit conversions from init([_] implicit:) constructors | ||
static int called = 0; | ||
static const char *from = 0 ? "\nFROM" : nullptr; | ||
auto trace = [](const char *prefix, Type ty) { | ||
if (!from) | ||
return; | ||
if (prefix == from) | ||
called++; | ||
fprintf(stderr, prefix == from ? "%s %p #%d " : "%s %p ", | ||
prefix, ty.getPointer(), called); | ||
ty->dump(); | ||
}; | ||
trace(from, fromType); | ||
trace("TO", toType); | ||
if (NominalTypeDecl *toNominal = toType->getAnyNominal()) { | ||
auto &ctx = getASTContext(); | ||
if (!toNominal->setConversionsComputed()) | ||
for (ExtensionDecl *extension : toNominal->getExtensions()) { | ||
Type extType = extension->getExtendedType()->getCanonicalType(); | ||
int arged = 0; | ||
for (Decl *member : extension->getMembers()) | ||
if (ConstructorDecl *initDecl = dyn_cast<ConstructorDecl>(member)) { | ||
ParameterList *parameters = initDecl->getParameters(); | ||
ParamDecl *param; // could be @implicit instead.. | ||
if (parameters->size() == 1 && (param = parameters->get(0)) && | ||
(param->getArgumentName() == ctx.Id_implicit || | ||
param->getParameterName() == ctx.Id_implicit)) { | ||
Type argType = param->getType()->getCanonicalType(); | ||
if (!arged++) | ||
trace("\nEXT", extType); | ||
trace(" ARG", argType); | ||
(*ctx.implicitConversionsTo(extType->getAnyNominal(), | ||
/*create*/true))[argType->getAnyNominal()].push_back(initDecl); | ||
} | ||
} | ||
} | ||
|
||
if (auto *exists = ctx.implicitConversionsTo(toNominal, /*create*/false)) | ||
for (ConstructorDecl *initDecl : (*exists)[fromType->getAnyNominal()]) | ||
if (ExtensionDecl *ext = dyn_cast<ExtensionDecl>(initDecl->getParent())) | ||
if (Type argType = initDecl->getParameters()->get(0)->getType()) | ||
if (Type extType = initDecl->getResultInterfaceType()) { | ||
extType = initDecl->getParent()->mapTypeIntoContext(extType); | ||
// More rigourous check of conversion here... | ||
trace("EXT2", extType); | ||
trace("ARG2", argType); | ||
if (argType->getCanonicalType() == fromType->getCanonicalType() || | ||
(extType->isUnsafeRawPointer() && | ||
(fromType->isUnsafeMutablePointer() || fromType->isUnsafePointer()))) { | ||
trace("SELECTED", initDecl->getInterfaceType()); | ||
return initDecl; | ||
} | ||
} | ||
} | ||
#else // Original hard coded rules for unsafe pointers. | ||
if (toType->isUnsafeRawPointer() && (fromType->isUnsafeMutableRawPointer() || | ||
fromType->isUnsafeMutablePointer() || fromType->isUnsafePointer())) { | ||
return true; // Unsafe[Mutable][Raw]Pointer -> UnsafeRawPointer | ||
} | ||
|
||
auto firstGenericArgument = [&](Type ty) -> CanType { | ||
return dyn_cast<BoundGenericStructType>(ty->getCanonicalType()) | ||
->getGenericArgs()[0]->getCanonicalType(); | ||
}; | ||
|
||
if (fromType->isUnsafeMutablePointer() && toType->isUnsafePointer() && | ||
firstGenericArgument(fromType) == firstGenericArgument(toType)) { | ||
toType->dump(); | ||
fromType->dump(); | ||
return true; // UnsafeMutablePointer<Pointee> -> UnsafePointer<Pointee> | ||
} | ||
#endif | ||
return nullptr; | ||
} | ||
|
||
ConstraintSystem::TypeMatchResult | ||
ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, | ||
TypeMatchOptions flags, | ||
|
@@ -5317,10 +5402,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, | |
} | ||
} | ||
|
||
if (kind >= ConstraintKind::Subtype && | ||
#if 0 | ||
if (0 && kind >= ConstraintKind::Subtype && | ||
nominal1->getDecl() != nominal2->getDecl() && | ||
((nominal1->isCGFloatType() || nominal2->isCGFloatType()) && | ||
(nominal1->isDouble() || nominal2->isDouble()))) { | ||
(nominal1->isDouble() || nominal2->isDouble())) || | ||
useRestrictions && implicitConversionAvailable(type1, type2)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please extract this into a separate check, no need to try to plug it into the Double/CGFloat. As a side note - if you are working on a more general mechanism then I think we need to remove Double/CGFloat specific logic all together from here because it would be possible to declare a |
||
ConstraintLocatorBuilder location{locator}; | ||
// Look through all value-to-optional promotions to allow | ||
// conversions like Double -> CGFloat?? and vice versa. | ||
|
@@ -5388,16 +5475,20 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, | |
rawElt.getAs<LocatorPathElt::ImplicitConversion>()) { | ||
auto convKind = elt->getConversionKind(); | ||
return convKind == ConversionRestrictionKind::DoubleToCGFloat || | ||
convKind == ConversionRestrictionKind::CGFloatToDouble; | ||
convKind == ConversionRestrictionKind::CGFloatToDouble || | ||
convKind == ConversionRestrictionKind::ImplicitConversion; | ||
} | ||
return false; | ||
})) { | ||
conversionsOrFixes.push_back( | ||
desugar1->isCGFloatType() | ||
? ConversionRestrictionKind::CGFloatToDouble | ||
: ConversionRestrictionKind::DoubleToCGFloat); | ||
// desugar1->isCGFloatType() | ||
// ? ConversionRestrictionKind::CGFloatToDouble : | ||
// desugar2->isCGFloatType() | ||
// ? ConversionRestrictionKind::DoubleToCGFloat : | ||
ConversionRestrictionKind::ImplicitConversion); | ||
} | ||
} | ||
#endif | ||
|
||
break; | ||
} | ||
|
@@ -11123,7 +11214,8 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( | |
} | ||
|
||
case ConversionRestrictionKind::DoubleToCGFloat: | ||
case ConversionRestrictionKind::CGFloatToDouble: { | ||
case ConversionRestrictionKind::CGFloatToDouble: | ||
case ConversionRestrictionKind::ImplicitConversion: { | ||
// Prefer CGFloat -> Double over other way araund. | ||
auto impact = | ||
restriction == ConversionRestrictionKind::CGFloatToDouble ? 1 : 10; | ||
|
@@ -11170,8 +11262,11 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( | |
memberTy, DC, FunctionRefKind::DoubleApply, | ||
/*outerAlternatives=*/{}, memberLoc); | ||
|
||
Identifier label; | ||
if (restriction == ConversionRestrictionKind::ImplicitConversion) | ||
label = getASTContext().Id_implicit; | ||
addConstraint(ConstraintKind::ApplicableFunction, | ||
FunctionType::get({FunctionType::Param(type1)}, type2), | ||
FunctionType::get({FunctionType::Param(type1, label)}, type2), | ||
memberTy, applicationLoc); | ||
|
||
ImplicitValueConversions.push_back( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation here would have to be more interesting, that's why we (me and @hborla) were talking about it being complex for the solver. We'd have to add a member constraint to lookup
init(implicit:)
on thefromType
and apply found member with an argumenttoType
to make sure that everything fits together correctly, so instead of doing manual lookup down here you'd have to always record an implicit conversion between types that didn't match but are constrained onConversion
and down insimplifyRestrictedConstraintImpl
implement it as member constraint + applicable function constraint.