Skip to content

Commit e7a4661

Browse files
authored
Merge pull request #60271 from hamishknight/invalid-operation
[Sema] Fix missing operator diagnostic logic
2 parents 04711ba + cfd2435 commit e7a4661

File tree

5 files changed

+55
-19
lines changed

5 files changed

+55
-19
lines changed

include/swift/AST/DeclContext.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,15 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
459459
const_cast<DeclContext *>(this)->getInnermostDeclarationDeclContext();
460460
}
461461

462+
/// Returns the topmost context that is a declaration, excluding ModuleDecl.
463+
///
464+
/// This may return itself.
465+
LLVM_READONLY
466+
Decl *getTopmostDeclarationDeclContext();
467+
const Decl *getTopmostDeclarationDeclContext() const {
468+
return const_cast<DeclContext *>(this)->getTopmostDeclarationDeclContext();
469+
}
470+
462471
/// Returns the innermost context that is an AbstractFunctionDecl whose
463472
/// body has been skipped.
464473
LLVM_READONLY

lib/AST/DeclContext.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,16 @@ Decl *DeclContext::getInnermostDeclarationDeclContext() {
237237
return nullptr;
238238
}
239239

240+
Decl *DeclContext::getTopmostDeclarationDeclContext() {
241+
auto *dc = this;
242+
Decl *topmost = nullptr;
243+
while (auto *decl = dc->getInnermostDeclarationDeclContext()) {
244+
topmost = decl;
245+
dc = decl->getDeclContext();
246+
}
247+
return topmost;
248+
}
249+
240250
DeclContext *DeclContext::getInnermostSkippedFunctionContext() {
241251
auto dc = this;
242252
do {

lib/Sema/TypeCheckDecl.cpp

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1942,25 +1942,10 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
19421942
}
19431943

19441944
if (!op) {
1945-
SourceLoc insertionLoc;
1946-
if (isa<SourceFile>(FD->getParent())) {
1947-
// Parent context is SourceFile, insertion location is start of func
1948-
// declaration or unary operator
1949-
if (FD->isUnaryOperator()) {
1950-
insertionLoc = FD->getAttrs().getStartLoc();
1951-
} else {
1952-
insertionLoc = FD->getStartLoc();
1953-
}
1954-
} else {
1955-
// Find the topmost non-file decl context and insert there.
1956-
for (DeclContext *CurContext = FD->getLocalContext();
1957-
!isa<SourceFile>(CurContext);
1958-
CurContext = CurContext->getParent()) {
1959-
// Skip over non-decl contexts (e.g. closure expressions)
1960-
if (auto *D = CurContext->getAsDecl())
1961-
insertionLoc = D->getStartLoc();
1962-
}
1963-
}
1945+
// We want to insert at the start of the top-most declaration, taking
1946+
// attributes into consideration.
1947+
auto *insertionDecl = FD->getTopmostDeclarationDeclContext();
1948+
auto insertionLoc = insertionDecl->getSourceRangeIncludingAttrs().Start;
19641949

19651950
SmallString<128> insertion;
19661951
{
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -verify -emit-module -experimental-allow-module-with-compiler-errors %s -o %t/foo.swiftmodule
4+
// RUN: %target-swift-frontend -verify -emit-module -module-name foo %t/foo.swiftmodule
5+
6+
// rdar://97267326 – Make sure we can handle an operator function without its declaration.
7+
struct S {
8+
static func ^^^ (lhs: S, rhs: S) {} // expected-error {{operator implementation without matching operator declaration}}
9+
}

test/decl/func/operator.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@ extension X {
3333
}
3434
}
3535

36+
// #60268: Make sure we insert at the start of the attributes.
37+
@discardableResult
38+
internal
39+
func ^^^ (lhs: Int, rhs: Int) -> Int {} // expected-error {{operator implementation without matching operator declaration}} {{37:1-1=infix operator ^^^ : <# Precedence Group #>\n}}
40+
41+
@discardableResult
42+
internal
43+
prefix func ^^^ (rhs: Int) -> Int {} // expected-error {{operator implementation without matching operator declaration}} {{41:1-1=prefix operator ^^^ : <# Precedence Group #>\n}}
44+
45+
@frozen
46+
public
47+
struct Z {
48+
struct Y {
49+
static func ^^^ (lhs: Y, rhs: Y) {} // expected-error {{operator implementation without matching operator declaration}} {{45:1-1=infix operator ^^^ : <# Precedence Group #>\n}}
50+
}
51+
}
52+
53+
_ = {
54+
func ^^^ (lhs: Int, rhs: Int) {}
55+
// expected-error@-1 {{operator functions can only be declared at global or in type scope}}
56+
// expected-error@-2 {{operator implementation without matching operator declaration}} {{53:1-1=infix operator ^^^ : <# Precedence Group #>\n}}
57+
}
58+
3659
infix operator ++++ : ReallyHighPrecedence
3760
precedencegroup ReallyHighPrecedence {
3861
higherThan: BitwiseShiftPrecedence

0 commit comments

Comments
 (0)