mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Implicitly look through UncheckedOptional<T> when it's the
base of a member access or subscript. Swift SVN r12345
This commit is contained in:
@@ -305,6 +305,17 @@ namespace {
|
|||||||
Expr *coerceViaUserConversion(Expr *expr, Type toType,
|
Expr *coerceViaUserConversion(Expr *expr, Type toType,
|
||||||
ConstraintLocatorBuilder locator);
|
ConstraintLocatorBuilder locator);
|
||||||
|
|
||||||
|
/// \brief Coerce an expression of (possibly unchecked) optional
|
||||||
|
/// type to have a different (possibly unchecked) optional type.
|
||||||
|
Expr *coerceOptionalToOptional(Expr *expr, Type toType,
|
||||||
|
ConstraintLocatorBuilder locator);
|
||||||
|
|
||||||
|
/// \brief Coerce an expression of unchecked optional type to its
|
||||||
|
/// underlying value type, in the correct way for an implicit
|
||||||
|
/// look-through.
|
||||||
|
Expr *coerceUncheckedOptionalToValue(Expr *expr, Type objTy,
|
||||||
|
ConstraintLocatorBuilder locator);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// \brief Build a reference to the given declaration.
|
/// \brief Build a reference to the given declaration.
|
||||||
Expr *buildDeclRef(ValueDecl *decl, SourceLoc loc, Type openedType,
|
Expr *buildDeclRef(ValueDecl *decl, SourceLoc loc, Type openedType,
|
||||||
@@ -355,9 +366,17 @@ namespace {
|
|||||||
auto &tc = cs.getTypeChecker();
|
auto &tc = cs.getTypeChecker();
|
||||||
auto &context = tc.Context;
|
auto &context = tc.Context;
|
||||||
|
|
||||||
|
Type baseTy = base->getType()->getRValueType();
|
||||||
|
|
||||||
|
// Handle accesses that implicitly look through UncheckedOptional<T>.
|
||||||
|
if (auto objTy = cs.lookThroughUncheckedOptionalType(baseTy)) {
|
||||||
|
base = coerceUncheckedOptionalToValue(base, objTy, locator);
|
||||||
|
if (!base) return nullptr;
|
||||||
|
baseTy = objTy;
|
||||||
|
}
|
||||||
|
|
||||||
// Figure out the actual base type, and whether we have an instance of
|
// Figure out the actual base type, and whether we have an instance of
|
||||||
// that type or its metatype.
|
// that type or its metatype.
|
||||||
Type baseTy = base->getType()->getRValueType();
|
|
||||||
bool baseIsInstance = true;
|
bool baseIsInstance = true;
|
||||||
if (auto baseMeta = baseTy->getAs<MetatypeType>()) {
|
if (auto baseMeta = baseTy->getAs<MetatypeType>()) {
|
||||||
baseIsInstance = false;
|
baseIsInstance = false;
|
||||||
@@ -677,6 +696,12 @@ namespace {
|
|||||||
auto &tc = cs.getTypeChecker();
|
auto &tc = cs.getTypeChecker();
|
||||||
auto baseTy = base->getType()->getRValueType();
|
auto baseTy = base->getType()->getRValueType();
|
||||||
|
|
||||||
|
// Handle accesses that implicitly look through UncheckedOptional<T>.
|
||||||
|
if (auto objTy = cs.lookThroughUncheckedOptionalType(baseTy)) {
|
||||||
|
base = coerceUncheckedOptionalToValue(base, objTy, locator);
|
||||||
|
if (!base) return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Figure out the index and result types.
|
// Figure out the index and result types.
|
||||||
auto containerTy
|
auto containerTy
|
||||||
= subscript->getDeclContext()->getDeclaredTypeOfContext();
|
= subscript->getDeclContext()->getDeclaredTypeOfContext();
|
||||||
@@ -1490,13 +1515,22 @@ namespace {
|
|||||||
selected.openedType,
|
selected.openedType,
|
||||||
cs.getConstraintLocator(expr, { }));
|
cs.getConstraintLocator(expr, { }));
|
||||||
|
|
||||||
case OverloadChoiceKind::TupleIndex:
|
case OverloadChoiceKind::TupleIndex: {
|
||||||
|
auto base = expr->getBase();
|
||||||
|
auto baseTy = base->getType()->getRValueType();
|
||||||
|
if (auto objTy = cs.lookThroughUncheckedOptionalType(baseTy)) {
|
||||||
|
base = coerceUncheckedOptionalToValue(base, objTy,
|
||||||
|
cs.getConstraintLocator(base, { }));
|
||||||
|
if (!base) return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return new (cs.getASTContext()) TupleElementExpr(
|
return new (cs.getASTContext()) TupleElementExpr(
|
||||||
expr->getBase(),
|
base,
|
||||||
expr->getDotLoc(),
|
expr->getDotLoc(),
|
||||||
selected.choice.getTupleIndex(),
|
selected.choice.getTupleIndex(),
|
||||||
expr->getNameLoc(),
|
expr->getNameLoc(),
|
||||||
simplifyType(expr->getType()));
|
simplifyType(expr->getType()));
|
||||||
|
}
|
||||||
|
|
||||||
case OverloadChoiceKind::BaseType: {
|
case OverloadChoiceKind::BaseType: {
|
||||||
// FIXME: Losing ".0" sugar here.
|
// FIXME: Losing ".0" sugar here.
|
||||||
@@ -1617,6 +1651,16 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expr *visitTupleElementExpr(TupleElementExpr *expr) {
|
Expr *visitTupleElementExpr(TupleElementExpr *expr) {
|
||||||
|
// Handle accesses that implicitly look through UncheckedOptional<T>.
|
||||||
|
auto base = expr->getBase();
|
||||||
|
auto baseTy = base->getType()->getRValueType();
|
||||||
|
if (auto objTy = cs.lookThroughUncheckedOptionalType(baseTy)) {
|
||||||
|
base = coerceUncheckedOptionalToValue(base, objTy,
|
||||||
|
cs.getConstraintLocator(base, { }));
|
||||||
|
if (!base) return nullptr;
|
||||||
|
expr->setBase(base);
|
||||||
|
}
|
||||||
|
|
||||||
simplifyExprType(expr);
|
simplifyExprType(expr);
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
@@ -2653,6 +2697,47 @@ Expr *ExprRewriter::coerceViaUserConversion(Expr *expr, Type toType,
|
|||||||
return coerceToType(expr, toType, locator);
|
return coerceToType(expr, toType, locator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType,
|
||||||
|
ConstraintLocatorBuilder locator) {
|
||||||
|
auto &tc = cs.getTypeChecker();
|
||||||
|
Type fromType = expr->getType();
|
||||||
|
|
||||||
|
auto fromGenericType = fromType->castTo<BoundGenericType>();
|
||||||
|
auto toGenericType = toType->castTo<BoundGenericType>();
|
||||||
|
assert(fromGenericType->getDecl()->classifyAsOptionalType());
|
||||||
|
assert(toGenericType->getDecl()->classifyAsOptionalType());
|
||||||
|
tc.requireOptionalIntrinsics(expr->getLoc());
|
||||||
|
|
||||||
|
Type fromValueType = fromGenericType->getGenericArgs()[0];
|
||||||
|
Type toValueType = toGenericType->getGenericArgs()[0];
|
||||||
|
|
||||||
|
expr = new (tc.Context) BindOptionalExpr(expr, expr->getSourceRange().End,
|
||||||
|
fromValueType);
|
||||||
|
expr->setImplicit(true);
|
||||||
|
expr = coerceToType(expr, toValueType, locator);
|
||||||
|
if (!expr) return nullptr;
|
||||||
|
|
||||||
|
expr = new (tc.Context) InjectIntoOptionalExpr(expr, toType);
|
||||||
|
|
||||||
|
expr = new (tc.Context) OptionalEvaluationExpr(expr, toType);
|
||||||
|
expr->setImplicit(true);
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr *ExprRewriter::coerceUncheckedOptionalToValue(Expr *expr, Type objTy,
|
||||||
|
ConstraintLocatorBuilder locator) {
|
||||||
|
// Coerce to an r-value.
|
||||||
|
auto rvalueTy = expr->getType()->getRValueType();
|
||||||
|
assert(rvalueTy->getUncheckedOptionalObjectType()->isEqual(objTy));
|
||||||
|
|
||||||
|
expr = coerceToType(expr, rvalueTy, /*bogus?*/ locator);
|
||||||
|
if (!expr) return nullptr;
|
||||||
|
|
||||||
|
expr = new (cs.getTypeChecker().Context) ForceValueExpr(expr, expr->getEndLoc());
|
||||||
|
expr->setType(objTy);
|
||||||
|
expr->setImplicit();
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
|
Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
|
||||||
ConstraintLocatorBuilder locator) {
|
ConstraintLocatorBuilder locator) {
|
||||||
@@ -2763,28 +2848,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ConversionRestrictionKind::UncheckedOptionalToOptional:
|
case ConversionRestrictionKind::UncheckedOptionalToOptional:
|
||||||
case ConversionRestrictionKind::OptionalToOptional: {
|
case ConversionRestrictionKind::OptionalToOptional:
|
||||||
auto fromGenericType = fromType->castTo<BoundGenericType>();
|
return coerceOptionalToOptional(expr, toType, locator);
|
||||||
auto toGenericType = toType->castTo<BoundGenericType>();
|
|
||||||
assert(fromGenericType->getDecl()->classifyAsOptionalType());
|
|
||||||
assert(toGenericType->getDecl()->classifyAsOptionalType());
|
|
||||||
tc.requireOptionalIntrinsics(expr->getLoc());
|
|
||||||
|
|
||||||
Type fromValueType = fromGenericType->getGenericArgs()[0];
|
|
||||||
Type toValueType = toGenericType->getGenericArgs()[0];
|
|
||||||
|
|
||||||
expr = new (tc.Context) BindOptionalExpr(expr, expr->getSourceRange().End,
|
|
||||||
fromValueType);
|
|
||||||
expr->setImplicit(true);
|
|
||||||
expr = coerceToType(expr, toValueType, locator);
|
|
||||||
if (!expr) return nullptr;
|
|
||||||
|
|
||||||
expr = new (tc.Context) InjectIntoOptionalExpr(expr, toType);
|
|
||||||
|
|
||||||
expr = new (tc.Context) OptionalEvaluationExpr(expr, toType);
|
|
||||||
expr->setImplicit(true);
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ConversionRestrictionKind::User:
|
case ConversionRestrictionKind::User:
|
||||||
return coerceViaUserConversion(expr, toType, locator);
|
return coerceViaUserConversion(expr, toType, locator);
|
||||||
|
|||||||
@@ -1328,6 +1328,11 @@ ConstraintSystem::simplifyMemberConstraint(const Constraint &constraint) {
|
|||||||
Type baseTy = simplifyType(constraint.getFirstType());
|
Type baseTy = simplifyType(constraint.getFirstType());
|
||||||
Type baseObjTy = baseTy->getRValueType();
|
Type baseObjTy = baseTy->getRValueType();
|
||||||
|
|
||||||
|
// Try to look through UncheckedOptional<T>; the result is always an r-value.
|
||||||
|
if (auto objTy = lookThroughUncheckedOptionalType(baseObjTy)) {
|
||||||
|
baseTy = baseObjTy = objTy;
|
||||||
|
}
|
||||||
|
|
||||||
// Dig out the instance type.
|
// Dig out the instance type.
|
||||||
bool isMetatype = false;
|
bool isMetatype = false;
|
||||||
Type instanceTy = baseObjTy;
|
Type instanceTy = baseObjTy;
|
||||||
|
|||||||
@@ -1055,6 +1055,45 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given that we're accessing a member of an UncheckedOptional<T>, is
|
||||||
|
/// the DC one of the special cases where we should not instead look at T?
|
||||||
|
static bool isPrivilegedAccessToUncheckedOptional(DeclContext *DC,
|
||||||
|
NominalTypeDecl *D) {
|
||||||
|
assert(D == DC->getASTContext().getUncheckedOptionalDecl());
|
||||||
|
|
||||||
|
// Walk up through the chain of current contexts.
|
||||||
|
for (; ; DC = DC->getParent()) {
|
||||||
|
assert(DC && "ran out of contexts before finding a module scope?");
|
||||||
|
|
||||||
|
// Look through local contexts.
|
||||||
|
if (DC->isLocalContext()) {
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If we're in a type context that's defining or extending
|
||||||
|
// UncheckedOptional<T>, we're privileged.
|
||||||
|
} else if (DC->isTypeContext()) {
|
||||||
|
if (DC->getDeclaredTypeInContext()->getAnyNominal() == D)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Otherwise, we're privileged if we're within the same file that
|
||||||
|
// defines UncheckedOptional<T>.
|
||||||
|
} else {
|
||||||
|
assert(DC->isModuleScopeContext());
|
||||||
|
return (DC == D->getModuleScopeContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type ConstraintSystem::lookThroughUncheckedOptionalType(Type type) {
|
||||||
|
if (auto boundTy = type->getAs<BoundGenericStructType>()) {
|
||||||
|
auto boundDecl = boundTy->getDecl();
|
||||||
|
if (boundDecl == TC.Context.getUncheckedOptionalDecl() &&
|
||||||
|
!isPrivilegedAccessToUncheckedOptional(DC, boundDecl))
|
||||||
|
return boundTy->getGenericArgs()[0];
|
||||||
|
}
|
||||||
|
return Type();
|
||||||
|
}
|
||||||
|
|
||||||
Type ConstraintSystem::simplifyType(Type type,
|
Type ConstraintSystem::simplifyType(Type type,
|
||||||
llvm::SmallPtrSet<TypeVariableType *, 16> &substituting) {
|
llvm::SmallPtrSet<TypeVariableType *, 16> &substituting) {
|
||||||
return type.transform([&](Type type) -> Type {
|
return type.transform([&](Type type) -> Type {
|
||||||
|
|||||||
@@ -1510,6 +1510,11 @@ public:
|
|||||||
void addOverloadSet(Type boundType, ArrayRef<OverloadChoice> choices,
|
void addOverloadSet(Type boundType, ArrayRef<OverloadChoice> choices,
|
||||||
ConstraintLocator *locator);
|
ConstraintLocator *locator);
|
||||||
|
|
||||||
|
/// If the given type is UncheckedOptional<T>, and we're in a context
|
||||||
|
/// that should transparently look through UncheckedOptional types,
|
||||||
|
/// return T.
|
||||||
|
Type lookThroughUncheckedOptionalType(Type type);
|
||||||
|
|
||||||
/// \brief Retrieve the allocator used by this constraint system.
|
/// \brief Retrieve the allocator used by this constraint system.
|
||||||
llvm::BumpPtrAllocator &getAllocator() { return Allocator; }
|
llvm::BumpPtrAllocator &getAllocator() { return Allocator; }
|
||||||
|
|
||||||
|
|||||||
51
test/Constraints/unchecked_optional.swift
Normal file
51
test/Constraints/unchecked_optional.swift
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// RUN: %swift -parse -verify %s
|
||||||
|
|
||||||
|
class A {
|
||||||
|
func do_a() {}
|
||||||
|
|
||||||
|
func do_b(x: Int) {}
|
||||||
|
func do_b(x: Float) {}
|
||||||
|
|
||||||
|
func do_c(x: Int) {} // expected-note {{found this candidate}}
|
||||||
|
func do_c(y: Int) {} // expected-note {{found this candidate}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test0(a : @unchecked A?) {
|
||||||
|
a.do_a()
|
||||||
|
|
||||||
|
a.do_b(1)
|
||||||
|
a.do_b(5.0)
|
||||||
|
|
||||||
|
a.do_c(1) // expected-error {{ambiguous use of 'do_c'}}
|
||||||
|
a.do_c(x: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test1(a : @unchecked A?) {
|
||||||
|
a?.do_a()
|
||||||
|
|
||||||
|
a?.do_b(1)
|
||||||
|
a?.do_b(5.0)
|
||||||
|
|
||||||
|
// FIXME: this should really get diagnosed like the above
|
||||||
|
a?.do_c(1) // expected-error {{expression does not type-check}}
|
||||||
|
a?.do_c(x: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
var x : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func test2(var b : @unchecked B?) {
|
||||||
|
var x = b.x
|
||||||
|
b.x = x // expected-error {{cannot assign to the result of this expression}}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Subscriptable {
|
||||||
|
subscript(x : Int) -> Int {
|
||||||
|
get: return x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test3(x: @unchecked Subscriptable?) -> Int {
|
||||||
|
return x[0]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user