Skip to content

Commit c99c02b

Browse files
committed
Transform EditorPlaceholderExpr into trap if executed in playground
mode (take 2) Allow untyped placeholder to take arbitrary type, but default to Void. Add _undefined<T>() function, which is like fatalError() but has arbitrary return type. In playground mode, merely warn about outstanding placeholders instead of erroring out, and transform placeholders into calls to _undefined(). This way, code with outstanding placeholders will only crash when it attempts to evaluate such placeholders. When generating constraints for an iterated sequence of type T, emit T convertible to $T1 $T1 conforms to SequenceType instead of T convertible to SequenceType This ensures that an untyped placeholder in for-each sequence position doesn't get inferred to have type SequenceType. (The conversion is still necessary because the sequence may have IUO type.) The new constraint system precipitates changes in CSSimplify and CSDiag, and ends up fixing 18741539 along the way. (NOTE: There is a small regression in diagnosis of issues like the following: class C {} class D: C {} func f(a: [C]!) { for _: D in a {} } It complains that [C]! doesn't conform to SequenceType when it should be complaining that C is not convertible to D.) <rdar://problem/21167372> (Originally Swift SVN r31481)
1 parent f6329bc commit c99c02b

27 files changed

+226
-51
lines changed

include/swift/AST/ASTContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,9 @@ class ASTContext {
481481
/// Retrieve the declaration of Swift._unimplemented_initializer.
482482
FuncDecl *getUnimplementedInitializerDecl(LazyResolver *resolver) const;
483483

484+
/// Retrieve the declaration of Swift._undefined.
485+
FuncDecl *getUndefinedDecl(LazyResolver *resolver) const;
486+
484487
// Retrieve the declaration of Swift._stdlib_isOSVersionAtLeast.
485488
FuncDecl *getIsOSVersionAtLeastDecl(LazyResolver *resolver) const;
486489

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ ERROR(lex_unary_postfix_dot_is_reserved,lexing,none,
132132
"postfix '.' is reserved", ())
133133
ERROR(lex_editor_placeholder,lexing,none,
134134
"editor placeholder in source file", ())
135+
WARNING(lex_editor_placeholder_in_playground,lexing,none,
136+
"editor placeholder in source file", ())
135137

136138
//------------------------------------------------------------------------------
137139
// Declaration parsing diagnostics

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,8 @@ NOTE(note_no_in_class_init_3plus,sema_tcd,none,
832832
(Identifier, Identifier, Identifier, bool))
833833
ERROR(missing_unimplemented_init_runtime,sema_tcd,none,
834834
"standard library error: missing _unimplemented_initializer", ())
835+
ERROR(missing_undefined_runtime,sema_tcd,none,
836+
"standard library error: missing _undefined", ())
835837
WARNING(unsupported_synthesize_init_variadic,sema_tcd,none,
836838
"synthesizing a variadic inherited initializer for subclass %0 is "
837839
"unsupported",

include/swift/AST/Expr.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3745,6 +3745,7 @@ class EditorPlaceholderExpr : public Expr {
37453745
SourceLoc Loc;
37463746
TypeLoc PlaceholderTy;
37473747
TypeRepr *ExpansionTyR;
3748+
Expr *SemanticExpr;
37483749

37493750
public:
37503751
EditorPlaceholderExpr(Identifier Placeholder, SourceLoc Loc,
@@ -3753,7 +3754,8 @@ class EditorPlaceholderExpr : public Expr {
37533754
: Expr(ExprKind::EditorPlaceholder, /*Implicit=*/false),
37543755
Placeholder(Placeholder), Loc(Loc),
37553756
PlaceholderTy(PlaceholderTy),
3756-
ExpansionTyR(ExpansionTyR) {
3757+
ExpansionTyR(ExpansionTyR),
3758+
SemanticExpr(nullptr) {
37573759
}
37583760

37593761
Identifier getPlaceholder() const { return Placeholder; }
@@ -3767,6 +3769,9 @@ class EditorPlaceholderExpr : public Expr {
37673769
static bool classof(const Expr *E) {
37683770
return E->getKind() == ExprKind::EditorPlaceholder;
37693771
}
3772+
3773+
Expr *getSemanticExpr() const { return SemanticExpr; }
3774+
void setSemanticExpr(Expr *SE) { SemanticExpr = SE; }
37703775
};
37713776

37723777
#undef SWIFT_FORWARD_SOURCE_LOCS_TO

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ EXPR(Assign, Expr)
156156
EXPR(DefaultValue, Expr)
157157
EXPR(CodeCompletion, Expr)
158158
UNCHECKED_EXPR(UnresolvedPattern, Expr)
159-
UNCHECKED_EXPR(EditorPlaceholder, Expr)
159+
EXPR(EditorPlaceholder, Expr)
160160

161161
#undef EXPR_RANGE
162162
#undef UNCHECKED_EXPR

lib/AST/ASTContext.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ struct ASTContext::Implementation {
177177
/// func _unimplemented_initializer(className: StaticString).
178178
FuncDecl *UnimplementedInitializerDecl = nullptr;
179179

180+
/// func _undefined<T>(msg: StaticString, file: StaticString, line: UInt) -> T
181+
FuncDecl *UndefinedDecl = nullptr;
182+
180183
/// func _stdlib_isOSVersionAtLeast(Builtin.Word,Builtin.Word, Builtin.word)
181184
// -> Builtin.Int1
182185
FuncDecl *IsOSVersionAtLeastDecl = nullptr;
@@ -985,6 +988,21 @@ ASTContext::getUnimplementedInitializerDecl(LazyResolver *resolver) const {
985988
return decl;
986989
}
987990

991+
FuncDecl *
992+
ASTContext::getUndefinedDecl(LazyResolver *resolver) const {
993+
if (Impl.UndefinedDecl)
994+
return Impl.UndefinedDecl;
995+
996+
// Look for the function.
997+
CanType input, output;
998+
auto decl = findLibraryIntrinsic(*this, "_undefined", resolver);
999+
if (!decl)
1000+
return nullptr;
1001+
1002+
Impl.UndefinedDecl = decl;
1003+
return decl;
1004+
}
1005+
9881006
FuncDecl *ASTContext::getIsOSVersionAtLeastDecl(LazyResolver *resolver) const {
9891007
if (Impl.IsOSVersionAtLeastDecl)
9901008
return Impl.IsOSVersionAtLeastDecl;

lib/AST/ASTWalker.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
824824
}
825825

826826
Expr *visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
827+
HANDLE_SEMANTIC_EXPR(E);
827828
return E;
828829
}
829830

lib/AST/Expr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ void Expr::propagateLValueAccessKind(AccessKind accessKind,
286286
LEAF_LVALUE_EXPR(DiscardAssignment)
287287
LEAF_LVALUE_EXPR(DynamicLookup)
288288
LEAF_LVALUE_EXPR(OpaqueValue)
289+
LEAF_LVALUE_EXPR(EditorPlaceholder)
289290

290291
COMPLETE_PHYSICAL_LVALUE_EXPR(AnyTry, getSubExpr())
291292
PARTIAL_PHYSICAL_LVALUE_EXPR(BindOptional, getSubExpr())

lib/AST/Verifier.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,8 @@ struct ASTNodeBase {};
462462
} while (!activeScope->isModuleScopeContext());
463463
}
464464

465-
Out << "AST verification error: archetype " << archetype
466-
<< " not allowed in this context\n";
465+
Out << "AST verification error: archetype "
466+
<< archetype->getString() << " not allowed in this context\n";
467467

468468
auto knownDC = Ctx.ArchetypeContexts.find(archetype);
469469
if (knownDC != Ctx.ArchetypeContexts.end()) {

lib/Parse/Lexer.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,9 +1279,13 @@ void Lexer::tryLexEditorPlaceholder() {
12791279
if (Ptr[0] == '<' && Ptr[1] == '#')
12801280
break;
12811281
if (Ptr[0] == '#' && Ptr[1] == '>') {
1282-
// Found it. Flag it as error for the rest of the compiler pipeline and
1283-
// lex it as an identifier.
1284-
diagnose(TokStart, diag::lex_editor_placeholder);
1282+
// Found it. Flag it as error (or warning, if in playground mode) for the
1283+
// rest of the compiler pipeline and lex it as an identifier.
1284+
if (LangOpts.Playground) {
1285+
diagnose(TokStart, diag::lex_editor_placeholder_in_playground);
1286+
} else {
1287+
diagnose(TokStart, diag::lex_editor_placeholder);
1288+
}
12851289
CurPtr = Ptr+2;
12861290
formToken(tok::identifier, TokStart);
12871291
return;

lib/SILGen/SILGenExpr.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ namespace {
196196
RValue visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *E,
197197
SGFContext C);
198198
RValue visitObjectLiteralExpr(ObjectLiteralExpr *E, SGFContext C);
199+
RValue visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, SGFContext C);
199200
RValue visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *E,
200201
SGFContext C);
201202
RValue visitCollectionExpr(CollectionExpr *E, SGFContext C);
@@ -1956,6 +1957,11 @@ visitObjectLiteralExpr(ObjectLiteralExpr *E, SGFContext C) {
19561957
return visit(E->getSemanticExpr(), C);
19571958
}
19581959

1960+
RValue RValueEmitter::
1961+
visitEditorPlaceholderExpr(EditorPlaceholderExpr *E, SGFContext C) {
1962+
return visit(E->getSemanticExpr(), C);
1963+
}
1964+
19591965
static StringRef
19601966
getMagicFunctionString(SILGenFunction &gen) {
19611967
assert(gen.MagicFunctionName

lib/Sema/CSApply.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,6 +3247,29 @@ namespace {
32473247
}
32483248

32493249
Expr *visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
3250+
Type valueType = simplifyType(E->getType());
3251+
E->setType(valueType);
3252+
3253+
auto &tc = cs.getTypeChecker();
3254+
auto &ctx = tc.Context;
3255+
// Synthesize a call to _undefined() of appropriate type.
3256+
FuncDecl *undefinedDecl = ctx.getUndefinedDecl(&tc);
3257+
if (!undefinedDecl) {
3258+
tc.diagnose(E->getLoc(), diag::missing_undefined_runtime);
3259+
return nullptr;
3260+
}
3261+
DeclRefExpr *fnRef = new (ctx) DeclRefExpr(undefinedDecl, SourceLoc(),
3262+
/*Implicit=*/true);
3263+
StringRef msg = "attempt to evaluate editor placeholder";
3264+
Expr *argExpr = new (ctx) StringLiteralExpr(msg, E->getLoc(),
3265+
/*implicit*/true);
3266+
argExpr = new (ctx) ParenExpr(E->getLoc(), argExpr, E->getLoc(),
3267+
/*hasTrailingClosure*/false);
3268+
Expr *callExpr = new (ctx) CallExpr(fnRef, argExpr, /*implicit*/true);
3269+
bool invalid = tc.typeCheckExpression(callExpr, cs.DC, valueType,
3270+
CTP_CannotFail);
3271+
assert(!invalid && "conversion cannot fail");
3272+
E->setSemanticExpr(callExpr);
32503273
return E;
32513274
}
32523275

lib/Sema/CSDiag.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,14 +2014,16 @@ static bool isConversionConstraint(const Constraint *C) {
20142014
/// low that we would only like to issue an error message about it if there is
20152015
/// nothing else interesting we can scrape out of the constraint system.
20162016
static bool isLowPriorityConstraint(Constraint *C) {
2017-
// If the member constraint is a ".Element" lookup to find the element type of
2018-
// a generator in a foreach loop, then it is very low priority: We will get a
2019-
// better and more useful diagnostic from the failed conversion to
2020-
// SequenceType that will fail as well.
2017+
// If the member constraint is a ".Generator" lookup to find the generator
2018+
// type in a foreach loop, or a ".Element" lookup to find its element type,
2019+
// then it is very low priority: We will get a better and more useful
2020+
// diagnostic from the failed conversion to SequenceType that will fail as
2021+
// well.
20212022
if (C->getKind() == ConstraintKind::TypeMember) {
20222023
if (auto *loc = C->getLocator())
20232024
for (auto Elt : loc->getPath())
2024-
if (Elt.getKind() == ConstraintLocator::GeneratorElementType)
2025+
if (Elt.getKind() == ConstraintLocator::GeneratorElementType ||
2026+
Elt.getKind() == ConstraintLocator::SequenceGeneratorType)
20252027
return true;
20262028
}
20272029

@@ -2457,7 +2459,7 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
24572459

24582460
Type fromType = CS->simplifyType(constraint->getFirstType());
24592461

2460-
if (fromType->is<TypeVariableType>() && resolvedAnchorToExpr) {
2462+
if (fromType->hasTypeVariable() && resolvedAnchorToExpr) {
24612463
TCCOptions options;
24622464

24632465
// If we know we're removing a contextual constraint, then we can force a
@@ -2548,12 +2550,16 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
25482550
return true;
25492551
}
25502552

2551-
// Emit a conformance error through conformsToProtocol. If this succeeds,
2552-
// then keep searching.
2553+
// Emit a conformance error through conformsToProtocol. If this succeeds
2554+
// and yields a valid protocol conformance, then keep searching.
2555+
ProtocolConformance *Conformance = nullptr;
25532556
if (CS->TC.conformsToProtocol(fromType, PT->getDecl(), CS->DC,
25542557
ConformanceCheckFlags::InExpression,
2555-
nullptr, expr->getLoc()))
2556-
return false;
2558+
&Conformance, expr->getLoc())) {
2559+
if (!Conformance || !Conformance->isInvalid()) {
2560+
return false;
2561+
}
2562+
}
25572563
return true;
25582564
}
25592565

lib/Sema/CSGen.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2471,9 +2471,20 @@ namespace {
24712471
}
24722472

24732473
Type visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
2474+
if (E->getTypeLoc().isNull()) {
2475+
auto locator = CS.getConstraintLocator(E);
2476+
auto placeholderTy = CS.createTypeVariable(locator, /*options*/0);
2477+
// A placeholder may have any type, but default to Void type if
2478+
// otherwise unconstrained.
2479+
CS.addConstraint(ConstraintKind::Defaultable,
2480+
placeholderTy, TupleType::getEmpty(CS.getASTContext()),
2481+
locator);
2482+
E->setType(placeholderTy);
2483+
}
2484+
// NOTE: The type loc may be there but have failed to validate, in which
2485+
// case we return the null type.
24742486
return E->getType();
24752487
}
2476-
24772488
};
24782489

24792490
/// \brief AST walker that "sanitizes" an expression for the

lib/Sema/CSSimplify.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,12 +2290,30 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
22902290
llvm_unreachable("bad constraint kind");
22912291
}
22922292

2293-
if (!type->getAnyOptionalObjectType().isNull() &&
2294-
protocol->isSpecificProtocol(KnownProtocolKind::BooleanType)) {
2295-
Fixes.push_back({FixKind::OptionalToBoolean,
2296-
getConstraintLocator(locator)});
2297-
2298-
return SolutionKind::Solved;
2293+
if (!shouldAttemptFixes())
2294+
return SolutionKind::Error;
2295+
2296+
// See if there's anything we can do to fix the conformance:
2297+
OptionalTypeKind optionalKind;
2298+
if (auto optionalObjectType = type->getAnyOptionalObjectType(optionalKind)) {
2299+
if (protocol->isSpecificProtocol(KnownProtocolKind::BooleanType)) {
2300+
// Optionals don't conform to BooleanType; suggest '!= nil'.
2301+
if (recordFix(FixKind::OptionalToBoolean, getConstraintLocator(locator)))
2302+
return SolutionKind::Error;
2303+
return SolutionKind::Solved;
2304+
} else if (optionalKind == OTK_Optional) {
2305+
// The underlying type of an optional may conform to the protocol if the
2306+
// optional doesn't; suggest forcing if that's the case.
2307+
auto result = simplifyConformsToConstraint(
2308+
optionalObjectType, protocol, kind,
2309+
locator.withPathElement(LocatorPathElt::getGenericArgument(0)), flags);
2310+
if (result == SolutionKind::Solved) {
2311+
if (recordFix(FixKind::ForceOptional, getConstraintLocator(locator))) {
2312+
return SolutionKind::Error;
2313+
}
2314+
}
2315+
return result;
2316+
}
22992317
}
23002318

23012319
// There's nothing more we can do; fail.

0 commit comments

Comments
 (0)