diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f6d6e26c7ba03..3e84049f014d0 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -7101,6 +7101,14 @@ ERROR(macro_in_nested,none, ERROR(macro_without_role,none, "macro %0 must declare its applicable roles via '@freestanding' or @attached'", (DeclName)) +ERROR(macro_result_type_cannot_be_used,none, + "only a freestanding expression macro can produce a result of type %0", + (Type)) +NOTE(macro_remove_result_type,none, + "remove the result type if the macro does not produce a value", + ()) +NOTE(macro_make_freestanding_expression,none, + "make this macro a freestanding expression macro", ()) ERROR(macro_expansion_missing_pound,none, "expansion of macro %0 requires leading '#'", (DeclName)) ERROR(macro_expansion_missing_arguments,none, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f13fd1591b0c3..c1954fd25efc1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3357,12 +3357,18 @@ TypeRepr *ValueDecl::getResultTypeRepr() const { returnRepr = FD->getResultTypeRepr(); } else if (auto *SD = dyn_cast(this)) { returnRepr = SD->getElementTypeRepr(); + } else if (auto *MD = dyn_cast(this)) { + returnRepr = MD->resultType.getTypeRepr(); } return returnRepr; } TypeRepr *ValueDecl::getOpaqueResultTypeRepr() const { + // FIXME: Macros don't allow opaque result types yet. + if (isa(this)) + return nullptr; + auto *returnRepr = this->getResultTypeRepr(); auto *dc = getDeclContext(); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 905f77175f42a..643b13afd8881 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2060,6 +2060,23 @@ class DeclChecker : public DeclVisitor { break; } } + + // If the macro has a (non-Void) result type, it must have the freestanding + // expression role. Other roles cannot have result types. + if (auto resultTypeRepr = MD->getResultTypeRepr()) { + if (!MD->getMacroRoles().contains(MacroRole::Expression) && + !MD->getResultInterfaceType()->isEqual(Ctx.getVoidType())) { + auto resultType = MD->getResultInterfaceType(); + Ctx.Diags.diagnose( + MD->arrowLoc, diag::macro_result_type_cannot_be_used, resultType) + .highlight(resultTypeRepr->getSourceRange()); + Ctx.Diags.diagnose(MD->arrowLoc, diag::macro_make_freestanding_expression) + .fixItInsert(MD->getAttributeInsertionLoc(false), + "@freestanding(expression)\n"); + Ctx.Diags.diagnose(MD->arrowLoc, diag::macro_remove_result_type) + .fixItRemove(SourceRange(MD->arrowLoc, resultTypeRepr->getEndLoc())); + } + } } void visitMacroExpansionDecl(MacroExpansionDecl *MED) { diff --git a/test/Macros/macros_diagnostics.swift b/test/Macros/macros_diagnostics.swift index 429ac87f77fc5..1955d9f6a76ec 100644 --- a/test/Macros/macros_diagnostics.swift +++ b/test/Macros/macros_diagnostics.swift @@ -46,7 +46,7 @@ internal struct X { } // expected-note{{type declared here}} // expected-warning@-1{{external macro implementation type}} struct ZZZ { - macro m5() -> Int = #externalMacro(module: "BuiltinMacros", type: "Blah") + macro m5() = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-error@-1{{macro 'm5()' can only be declared at file scope}} // expected-error@-2{{macro 'm5()' must declare its applicable roles}} // expected-warning@-3{{external macro implementation type}} @@ -200,3 +200,14 @@ struct SomeType { // expected-error@-2{{use of protocol 'Hashable' as a type must be written 'any Hashable'}} // expected-error@-3{{external macro implementation type}} } + + + +@freestanding(declaration) macro nonExpressionReturnsInt(_: T) -> Int = #externalMacro(module: "A", type: "B") +// expected-warning@-1{{external macro implementation type}} +// expected-error@-2{{only a freestanding expression macro can produce a result of type 'Int'}} +// expected-note@-3{{make this macro a freestanding expression macro}}{{1-1=@freestanding(expression)\n}} +// expected-note@-4{{remove the result type if the macro does not produce a value}}{{67-74=}} + +@freestanding(declaration) macro nonExpressionReturnsVoid(_: T) -> Void = #externalMacro(module: "A", type: "B") +// expected-warning@-1{{external macro implementation type}}