Skip to content

Commit ad5fdf7

Browse files
authored
Merge pull request #61066 from LucianoPAlmeida/diagnose-operator-crash
[Sema] Add param indicate diagnose or not in lookupPrecedenceGroupForInfixOperator
2 parents 32417d1 + 2b5054f commit ad5fdf7

File tree

4 files changed

+50
-23
lines changed

4 files changed

+50
-23
lines changed

lib/Sema/CSApply.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8116,7 +8116,8 @@ bool swift::exprNeedsParensInsideFollowingOperator(
81168116
DeclContext *DC, Expr *expr,
81178117
PrecedenceGroupDecl *followingPG) {
81188118
if (expr->isInfixOperator()) {
8119-
auto exprPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, expr);
8119+
auto exprPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(
8120+
DC, expr, /*diagnose=*/false);
81208121
if (!exprPG) return true;
81218122

81228123
return DC->getASTContext().associateInfixOperators(exprPG, followingPG)
@@ -8166,8 +8167,8 @@ bool swift::exprNeedsParensOutsideFollowingOperator(
81668167
return false;
81678168

81688169
if (parent->isInfixOperator()) {
8169-
auto parentPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC,
8170-
parent);
8170+
auto parentPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(
8171+
DC, parent, /*diagnose=*/false);
81718172
if (!parentPG) return true;
81728173

81738174
// If the index is 0, this is on the LHS of the parent.

lib/Sema/TypeCheckExpr.cpp

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -107,73 +107,84 @@ Expr *TypeChecker::substituteInputSugarTypeForResult(ApplyExpr *E) {
107107
static PrecedenceGroupDecl *lookupPrecedenceGroupForOperator(DeclContext *DC,
108108
Identifier name,
109109
SourceLoc loc) {
110-
auto *op = DC->lookupInfixOperator(name).getSingleOrDiagnose(loc);
110+
auto result = DC->lookupInfixOperator(name);
111+
auto *op =
112+
loc.isValid() ? result.getSingleOrDiagnose(loc) : result.getSingle();
111113
return op ? op->getPrecedenceGroup() : nullptr;
112114
}
113115

114116
PrecedenceGroupDecl *
115-
TypeChecker::lookupPrecedenceGroupForInfixOperator(DeclContext *DC, Expr *E) {
117+
TypeChecker::lookupPrecedenceGroupForInfixOperator(DeclContext *DC, Expr *E,
118+
bool diagnose) {
116119
/// Look up the builtin precedence group with the given name.
117120

118121
auto getBuiltinPrecedenceGroup = [&](DeclContext *DC, Identifier name,
119122
SourceLoc loc) -> PrecedenceGroupDecl * {
120123
auto groups = TypeChecker::lookupPrecedenceGroup(DC, name, loc);
121-
return groups.getSingleOrDiagnose(loc, /*forBuiltin*/ true);
124+
return loc.isValid() ? groups.getSingleOrDiagnose(loc, /*forBuiltin*/ true)
125+
: groups.getSingle();
122126
};
123127

124128
auto &Context = DC->getASTContext();
125129
if (auto ifExpr = dyn_cast<IfExpr>(E)) {
126130
// Ternary has fixed precedence.
127131
return getBuiltinPrecedenceGroup(DC, Context.Id_TernaryPrecedence,
128-
ifExpr->getQuestionLoc());
132+
diagnose ? ifExpr->getQuestionLoc()
133+
: SourceLoc());
129134
}
130135

131136
if (auto assignExpr = dyn_cast<AssignExpr>(E)) {
132137
// Assignment has fixed precedence.
133138
return getBuiltinPrecedenceGroup(DC, Context.Id_AssignmentPrecedence,
134-
assignExpr->getEqualLoc());
139+
diagnose ? assignExpr->getEqualLoc()
140+
: SourceLoc());
135141
}
136142

137143
if (auto castExpr = dyn_cast<ExplicitCastExpr>(E)) {
138144
// 'as' and 'is' casts have fixed precedence.
139145
return getBuiltinPrecedenceGroup(DC, Context.Id_CastingPrecedence,
140-
castExpr->getAsLoc());
146+
diagnose ? castExpr->getAsLoc()
147+
: SourceLoc());
141148
}
142149

143150
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
144151
Identifier name = DRE->getDecl()->getBaseIdentifier();
145-
return lookupPrecedenceGroupForOperator(DC, name, DRE->getLoc());
152+
return lookupPrecedenceGroupForOperator(
153+
DC, name, diagnose ? DRE->getLoc() : SourceLoc());
146154
}
147155

148156
if (auto *OO = dyn_cast<OverloadedDeclRefExpr>(E)) {
149157
Identifier name = OO->getDecls()[0]->getBaseIdentifier();
150-
return lookupPrecedenceGroupForOperator(DC, name, OO->getLoc());
158+
return lookupPrecedenceGroupForOperator(
159+
DC, name, diagnose ? OO->getLoc() : SourceLoc());
151160
}
152161

153162
if (auto arrowExpr = dyn_cast<ArrowExpr>(E)) {
154-
return getBuiltinPrecedenceGroup(DC,
155-
Context.Id_FunctionArrowPrecedence,
156-
arrowExpr->getArrowLoc());
163+
return getBuiltinPrecedenceGroup(DC, Context.Id_FunctionArrowPrecedence,
164+
diagnose ? arrowExpr->getArrowLoc()
165+
: SourceLoc());
157166
}
158167

159168
// An already-folded binary operator comes up for non-primary use cases
160169
// of this function.
161170
if (auto binaryExpr = dyn_cast<BinaryExpr>(E)) {
162-
return lookupPrecedenceGroupForInfixOperator(DC, binaryExpr->getFn());
171+
return lookupPrecedenceGroupForInfixOperator(DC, binaryExpr->getFn(),
172+
diagnose);
163173
}
164174

165175
if (auto *DSCE = dyn_cast<DotSyntaxCallExpr>(E)) {
166-
return lookupPrecedenceGroupForInfixOperator(DC, DSCE->getFn());
176+
return lookupPrecedenceGroupForInfixOperator(DC, DSCE->getFn(), diagnose);
167177
}
168178

169179
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
170180
Identifier name = MRE->getDecl().getDecl()->getBaseIdentifier();
171-
return lookupPrecedenceGroupForOperator(DC, name, MRE->getLoc());
181+
return lookupPrecedenceGroupForOperator(
182+
DC, name, diagnose ? MRE->getLoc() : SourceLoc());
172183
}
173184

174185
// If E is already an ErrorExpr, then we've diagnosed it as invalid already,
175186
// otherwise emit an error.
176-
if (!isa<ErrorExpr>(E))
187+
if (diagnose && !isa<ErrorExpr>(E))
177188
Context.Diags.diagnose(E->getLoc(), diag::unknown_binop);
178189

179190
return nullptr;
@@ -203,7 +214,7 @@ Expr *TypeChecker::findLHS(DeclContext *DC, Expr *E, Identifier name) {
203214
continue;
204215
}
205216

206-
auto left = lookupPrecedenceGroupForInfixOperator(DC, E);
217+
auto left = lookupPrecedenceGroupForInfixOperator(DC, E, /*diagnose=*/true);
207218
if (!left)
208219
// LHS is not binary expression.
209220
return E;
@@ -425,7 +436,8 @@ static Expr *foldSequence(DeclContext *DC,
425436
Expr *op = S[0];
426437

427438
// If the operator's precedence is lower than the minimum, stop here.
428-
auto opPrecedence = TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, op);
439+
auto opPrecedence = TypeChecker::lookupPrecedenceGroupForInfixOperator(
440+
DC, op, /*diagnose=*/true);
429441
if (!precedenceBound.shouldConsider(opPrecedence))
430442
return {nullptr, nullptr};
431443
return {op, opPrecedence};
@@ -457,7 +469,8 @@ static Expr *foldSequence(DeclContext *DC,
457469
}
458470

459471
// Pull out the next binary operator.
460-
Op op2{ S[0], TypeChecker::lookupPrecedenceGroupForInfixOperator(DC, S[0]) };
472+
Op op2{S[0], TypeChecker::lookupPrecedenceGroupForInfixOperator(
473+
DC, S[0], /*diagnose=*/true)};
461474

462475
// If the second operator's precedence is lower than the
463476
// precedence bound, break out of the loop.

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -905,8 +905,8 @@ lookupMemberType(DeclContext *dc, Type type, DeclNameRef name,
905905

906906
/// Given an expression that's known to be an infix operator,
907907
/// look up its precedence group.
908-
PrecedenceGroupDecl *lookupPrecedenceGroupForInfixOperator(DeclContext *dc,
909-
Expr *op);
908+
PrecedenceGroupDecl *
909+
lookupPrecedenceGroupForInfixOperator(DeclContext *dc, Expr *op, bool diagnose);
910910

911911
PrecedenceGroupLookupResult
912912
lookupPrecedenceGroup(DeclContext *dc, Identifier name, SourceLoc nameLoc);

test/Constraints/operator.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,16 @@ do {
314314
let number = 1
315315
let test = true || number == .First.rawValue // expected-error {{type 'Int' has no member 'First'}}
316316
}
317+
318+
// https://github.com/apple/swift/issues/60954
319+
enum I60954 {
320+
// expected-error@+1{{operator implementation without matching operator declaration}}
321+
func ?= (pattern: I60954?, version: Self) { // expected-error{{operator '?=' declared in type 'I60954' must be 'static'}}
322+
// expected-error@+2{{operator is not a known binary operator}}
323+
// expected-error@+1{{initializer 'init(_:)' requires that 'I60954' conform to 'StringProtocol'}}
324+
pattern ?= .init(version) // expected-error{{value of optional type 'I60954?' must be unwrapped to a value of type 'I60954'}}
325+
// expected-note@-1{{coalesce using '??' to provide a default when the optional value contains 'nil'}}
326+
// expected-note@-2{{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
327+
}
328+
init?<S>(_ string: S) where S: StringProtocol {} // expected-note{{where 'S' = 'I60954'}}
329+
}

0 commit comments

Comments
 (0)