diff --git a/include/swift/AST/DefaultArgumentKind.h b/include/swift/AST/DefaultArgumentKind.h index d9b571baf14..ee9781828e9 100644 --- a/include/swift/AST/DefaultArgumentKind.h +++ b/include/swift/AST/DefaultArgumentKind.h @@ -51,6 +51,8 @@ enum class DefaultArgumentKind : uint8_t { // Magic identifier literals expanded at the call site: #define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) NAME, #include "swift/AST/MagicIdentifierKinds.def" + /// An expression macro. + ExpressionMacro }; enum { NumDefaultArgumentKindBits = 4 }; diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 8b7dccc5807..f5de567aef7 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -106,6 +106,7 @@ enum class SourceFileKind { SIL, ///< Came from a .sil file. Interface, ///< Came from a .swiftinterface file, representing another module. MacroExpansion, ///< Came from a macro expansion. + DefaultArgument, ///< Came from default argument at caller side }; /// Contains information about where a particular path is used in diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index ee4874f5ae5..4d95bf7315f 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -674,6 +674,7 @@ public: case SourceFileKind::Interface: case SourceFileKind::SIL: case SourceFileKind::MacroExpansion: + case SourceFileKind::DefaultArgument: return false; } llvm_unreachable("bad SourceFileKind"); diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index aec28b32d33..ea6ebaac7b5 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -135,6 +135,7 @@ EXPERIMENTAL_FEATURE(NamedOpaqueTypes, false) EXPERIMENTAL_FEATURE(FlowSensitiveConcurrencyCaptures, false) EXPERIMENTAL_FEATURE(CodeItemMacros, false) EXPERIMENTAL_FEATURE(BodyMacros, true) +EXPERIMENTAL_FEATURE(ExpressionMacroDefaultArguments, false) EXPERIMENTAL_FEATURE(TupleConformances, false) // Whether to enable @_used and @_section attributes diff --git a/include/swift/Basic/MacroRoles.def b/include/swift/Basic/MacroRoles.def index 372e50f3157..eaee94a6728 100644 --- a/include/swift/Basic/MacroRoles.def +++ b/include/swift/Basic/MacroRoles.def @@ -70,6 +70,9 @@ ATTACHED_MACRO_ROLE(Conformance, "conformance", "c") /// declarations in a code block. EXPERIMENTAL_FREESTANDING_MACRO_ROLE(CodeItem, "codeItem", CodeItemMacros) +/// A freestanding macro that is used as the default argument +EXPERIMENTAL_FREESTANDING_MACRO_ROLE(DefaultArgument, "defaultArgument", ExpressionMacroDefaultArguments) + /// An attached macro that adds extensions to the declaration the /// macro is attached to. ATTACHED_MACRO_ROLE(Extension, "extension", "e") diff --git a/include/swift/Basic/SourceManager.h b/include/swift/Basic/SourceManager.h index 8b4e7cb7d78..9c906f6cb47 100644 --- a/include/swift/Basic/SourceManager.h +++ b/include/swift/Basic/SourceManager.h @@ -43,6 +43,9 @@ public: /// Pretty-printed declarations that have no source location. PrettyPrinted, + + /// The expansion of default argument at caller side + DefaultArgument, } kind; /// The source range in the enclosing buffer where this source was generated. diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 7937a388660..21e1e4b17bf 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -85,7 +85,7 @@ llvm::Optional typeCheckTarget(constraints::SyntacticElementTarget &target, OptionSet options); -Type typeCheckParameterDefault(Expr *&, DeclContext *, Type, bool); +Type typeCheckParameterDefault(Expr *&, DeclContext *, Type, bool, bool); } // end namespace TypeChecker @@ -2970,7 +2970,7 @@ private: friend Type swift::TypeChecker::typeCheckParameterDefault(Expr *&, DeclContext *, Type, - bool); + bool, bool); /// Emit the fixes computed as part of the solution, returning true if we were /// able to emit an error message, or false if none of the fixits worked out. diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 5f235df84c2..262c2da34f1 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -309,6 +309,8 @@ static StringRef getDumpString(DefaultArgumentKind value) { case DefaultArgumentKind::EmptyDictionary: return "[:]"; case DefaultArgumentKind::Normal: return "normal"; case DefaultArgumentKind::StoredProperty: return "stored property"; + case DefaultArgumentKind::ExpressionMacro: + return "expression macro"; } llvm_unreachable("Unhandled DefaultArgumentKind in switch."); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 2983a40a0b2..cc77e924586 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -3877,6 +3877,8 @@ void ASTMangler::appendMacroExpansionContext( case GeneratedSourceInfo::PrettyPrinted: case GeneratedSourceInfo::ReplacedFunctionBody: + // TODO: ApolloZhu check if this correct? + case GeneratedSourceInfo::DefaultArgument: return appendContext(origDC, StringRef()); } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index a9dd76d6363..1995e5bbc9e 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3042,6 +3042,18 @@ static bool usesFeatureFreestandingMacros(Decl *decl) { return macro->getMacroRoles().contains(MacroRole::Declaration); } +static bool usesFeatureExpressionMacroDefaultArguments(Decl *decl) { + if (auto func = dyn_cast(decl)) { + for (auto param : *func->getParameters()) { + if (param->getDefaultArgumentKind() == + DefaultArgumentKind::ExpressionMacro) + return true; + } + } + + return false; +} + static bool usesFeatureCodeItemMacros(Decl *decl) { auto macro = dyn_cast(decl); if (!macro) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 2a46e881b5f..d790e494d9a 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -8290,6 +8290,9 @@ DefaultArgumentKind swift::getDefaultArgKind(Expr *init) { if (isa(init)) return DefaultArgumentKind::NilLiteral; + if (isa(init)) + return DefaultArgumentKind::ExpressionMacro; + auto magic = dyn_cast(init); if (!magic) return DefaultArgumentKind::Normal; @@ -8489,6 +8492,7 @@ bool ParamDecl::hasDefaultExpr() const { #define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ case DefaultArgumentKind::NAME: #include "swift/AST/MagicIdentifierKinds.def" + case DefaultArgumentKind::ExpressionMacro: case DefaultArgumentKind::NilLiteral: case DefaultArgumentKind::EmptyArray: case DefaultArgumentKind::EmptyDictionary: @@ -8512,6 +8516,7 @@ bool ParamDecl::hasCallerSideDefaultExpr() const { case DefaultArgumentKind::NilLiteral: case DefaultArgumentKind::EmptyArray: case DefaultArgumentKind::EmptyDictionary: + case DefaultArgumentKind::ExpressionMacro: return true; } llvm_unreachable("invalid default argument kind"); @@ -8708,6 +8713,7 @@ ParamDecl::getDefaultValueStringRepresentation( switch (getDefaultArgumentKind()) { case DefaultArgumentKind::None: llvm_unreachable("called on a ParamDecl with no default value"); + case DefaultArgumentKind::ExpressionMacro: case DefaultArgumentKind::Normal: { assert(DefaultValueAndFlags.getPointer() && "default value not provided yet"); @@ -8800,7 +8806,8 @@ ParamDecl::getDefaultValueStringRepresentation( void ParamDecl::setDefaultValueStringRepresentation(StringRef stringRepresentation) { assert(getDefaultArgumentKind() == DefaultArgumentKind::Normal || - getDefaultArgumentKind() == DefaultArgumentKind::StoredProperty); + getDefaultArgumentKind() == DefaultArgumentKind::StoredProperty || + getDefaultArgumentKind() == DefaultArgumentKind::ExpressionMacro); assert(!stringRepresentation.empty()); if (!DefaultValueAndFlags.getPointer()) { @@ -12008,6 +12015,7 @@ MacroDiscriminatorContext MacroDiscriminatorContext::getParentOf( #include "swift/Basic/MacroRoles.def" case GeneratedSourceInfo::PrettyPrinted: case GeneratedSourceInfo::ReplacedFunctionBody: + case GeneratedSourceInfo::DefaultArgument: return origDC; } } diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index f48061066e8..d8eab4bce4d 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -1392,6 +1392,7 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) { case GeneratedSourceInfo::Name##MacroExpansion: #include "swift/Basic/MacroRoles.def" case GeneratedSourceInfo::PrettyPrinted: + case GeneratedSourceInfo::DefaultArgument: fixIts = {}; break; case GeneratedSourceInfo::ReplacedFunctionBody: @@ -1465,6 +1466,7 @@ DiagnosticEngine::getGeneratedSourceBufferNotes(SourceLoc loc) { case GeneratedSourceInfo::PrettyPrinted: break; + case GeneratedSourceInfo::DefaultArgument: case GeneratedSourceInfo::ReplacedFunctionBody: return childNotes; } @@ -1648,6 +1650,7 @@ swift::getGeneratedSourceInfoMacroName(const GeneratedSourceInfo &info) { case GeneratedSourceInfo::PrettyPrinted: case GeneratedSourceInfo::ReplacedFunctionBody: - return DeclName(); + case GeneratedSourceInfo::DefaultArgument: + return DeclName(); } } diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index b6db331fedb..bf1b8ae9a18 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1214,6 +1214,7 @@ llvm::Optional SourceFile::getFulfilledMacroRole() const { case GeneratedSourceInfo::ReplacedFunctionBody: case GeneratedSourceInfo::PrettyPrinted: + case GeneratedSourceInfo::DefaultArgument: return llvm::None; } } @@ -3308,7 +3309,8 @@ SourceFile::SourceFile(ModuleDecl &M, SourceFileKind K, (void)problem; } - if (Kind == SourceFileKind::MacroExpansion) + if (Kind == SourceFileKind::MacroExpansion || + Kind == SourceFileKind::DefaultArgument) M.addAuxiliaryFile(*this); } diff --git a/lib/Basic/SourceLoc.cpp b/lib/Basic/SourceLoc.cpp index 4cfd9a1f3ac..b89bfbab7fc 100644 --- a/lib/Basic/SourceLoc.cpp +++ b/lib/Basic/SourceLoc.cpp @@ -289,7 +289,8 @@ StringRef SourceManager::getIdentifierForBuffer( if (auto generatedInfo = getGeneratedSourceInfo(bufferID)) { // We only care about macros, so skip everything else. if (generatedInfo->kind == GeneratedSourceInfo::ReplacedFunctionBody || - generatedInfo->kind == GeneratedSourceInfo::PrettyPrinted) + generatedInfo->kind == GeneratedSourceInfo::PrettyPrinted || + generatedInfo->kind == GeneratedSourceInfo::DefaultArgument) return buffer->getBufferIdentifier(); if (generatedInfo->onDiskBufferCopyFileName.empty()) { @@ -382,6 +383,7 @@ void SourceManager::setGeneratedSourceInfo( case GeneratedSourceInfo::Name##MacroExpansion: #include "swift/Basic/MacroRoles.def" case GeneratedSourceInfo::PrettyPrinted: + case GeneratedSourceInfo::DefaultArgument: break; case GeneratedSourceInfo::ReplacedFunctionBody: diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index f3384cb583f..176546a989f 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -1006,6 +1006,7 @@ bool CompletionLookup::hasInterestingDefaultValue(const ParamDecl *param) { #define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ case DefaultArgumentKind::NAME: #include "swift/AST/MagicIdentifierKinds.def" + case DefaultArgumentKind::ExpressionMacro: return false; } } diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index 96bd1489462..e2900c8d7ac 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -658,6 +658,7 @@ adjustMacroExpansionWhitespace(GeneratedSourceInfo::Kind kind, case GeneratedSourceInfo::BodyMacroExpansion: case GeneratedSourceInfo::ReplacedFunctionBody: case GeneratedSourceInfo::PrettyPrinted: + case GeneratedSourceInfo::DefaultArgument: return expandedCode; } } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index c3a73000bbc..e564cbf6d94 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -218,6 +218,7 @@ void Parser::parseTopLevelItems(SmallVectorImpl &items) { break; case SourceFileKind::MacroExpansion: + case SourceFileKind::DefaultArgument: braceItemListKind = BraceItemListKind::MacroExpansion; break; } @@ -9932,6 +9933,7 @@ parseDeclDeinit(ParseDeclOptions Flags, DeclAttributes &Attributes) { case SourceFileKind::Library: case SourceFileKind::Main: case SourceFileKind::MacroExpansion: + case SourceFileKind::DefaultArgument: if (Tok.is(tok::identifier)) { diagnose(Tok, diag::destructor_has_name).fixItRemove(Tok.getLoc()); consumeToken(); diff --git a/lib/Parse/ParseRequests.cpp b/lib/Parse/ParseRequests.cpp index 00cd09d0c6a..1f6d1cc1d53 100644 --- a/lib/Parse/ParseRequests.cpp +++ b/lib/Parse/ParseRequests.cpp @@ -183,7 +183,8 @@ SourceFileParsingResult ParseSourceFileRequest::evaluate(Evaluator &evaluator, case GeneratedSourceInfo::ExpressionMacroExpansion: case GeneratedSourceInfo::PreambleMacroExpansion: case GeneratedSourceInfo::ReplacedFunctionBody: - case GeneratedSourceInfo::PrettyPrinted: { + case GeneratedSourceInfo::PrettyPrinted: + case GeneratedSourceInfo::DefaultArgument: { parser.parseTopLevelItems(items); break; } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 52288e0ea3f..7ff3e23e2c1 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -325,6 +325,7 @@ static LexerMode sourceFileKindToLexerMode(SourceFileKind kind) { case swift::SourceFileKind::Library: case swift::SourceFileKind::Main: case swift::SourceFileKind::MacroExpansion: + case swift::SourceFileKind::DefaultArgument: return LexerMode::Swift; } llvm_unreachable("covered switch"); diff --git a/lib/Refactoring/Refactoring.cpp b/lib/Refactoring/Refactoring.cpp index 58c4650ebfa..afd26e5b797 100644 --- a/lib/Refactoring/Refactoring.cpp +++ b/lib/Refactoring/Refactoring.cpp @@ -155,7 +155,8 @@ swift::ide::collectRefactorings(SourceFile *SF, RangeConfig Range, DiagConsumers); // No refactorings are available within generated buffers - if (SF->Kind == SourceFileKind::MacroExpansion) + if (SF->Kind == SourceFileKind::MacroExpansion || + SF->Kind == SourceFileKind::DefaultArgument) return {}; // Prepare the tool box. diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 5626288ac40..bd80097948e 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1617,6 +1617,7 @@ void SILGenModule::emitDefaultArgGenerator(SILDeclRef constant, case DefaultArgumentKind::NilLiteral: case DefaultArgumentKind::EmptyArray: case DefaultArgumentKind::EmptyDictionary: + case DefaultArgumentKind::ExpressionMacro: break; } } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 20cda981a86..8efa59d1ba0 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -344,6 +344,7 @@ static MacroInfo getMacroInfo(GeneratedSourceInfo &Info, } case GeneratedSourceInfo::PrettyPrinted: case GeneratedSourceInfo::ReplacedFunctionBody: + case GeneratedSourceInfo::DefaultArgument: break; } return Result; diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 2fb59c472fd..fa9fe468e37 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -785,6 +785,7 @@ addDistributedActorCodableConformance( case SourceFileKind::Main: case SourceFileKind::MacroExpansion: case SourceFileKind::SIL: + case SourceFileKind::DefaultArgument: break; } } diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index bfc5202bbae..8bacaa91c47 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -615,6 +615,7 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, case DefaultArgumentKind::EmptyArray: case DefaultArgumentKind::EmptyDictionary: case DefaultArgumentKind::StoredProperty: + case DefaultArgumentKind::ExpressionMacro: return llvm::None; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index d37cd02baa4..c4b82315a6e 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -45,6 +45,7 @@ static bool shouldInferAttributeInContext(const DeclContext *dc) { // Interfaces have explicitly called-out Sendable conformances. return false; + case SourceFileKind::DefaultArgument: case SourceFileKind::Library: case SourceFileKind::MacroExpansion: case SourceFileKind::Main: @@ -868,6 +869,7 @@ static bool shouldDiagnosePreconcurrencyImports(SourceFile &sf) { case SourceFileKind::SIL: return false; + case SourceFileKind::DefaultArgument: case SourceFileKind::Library: case SourceFileKind::Main: case SourceFileKind::MacroExpansion: @@ -5615,6 +5617,7 @@ ProtocolConformance *swift::deriveImplicitSendableConformance( // Interfaces have explicitly called-out Sendable conformances. return nullptr; + case SourceFileKind::DefaultArgument: case SourceFileKind::Library: case SourceFileKind::MacroExpansion: case SourceFileKind::Main: diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index d0b83e14e7e..8a4a22e924e 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -545,7 +545,8 @@ TypeChecker::typeCheckTarget(SyntacticElementTarget &target, Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, Type paramType, - bool isAutoClosure) { + bool isAutoClosure, + bool atCallerSide) { // During normal type checking we don't type check the parameter default if // the param has an error type. For code completion, we also type check the // parameter default because it might contain the code completion token. @@ -569,10 +570,16 @@ Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, // different contextual type, see below. DiagnosticTransaction diagnostics(ctx.Diags); + TypeCheckExprOptions options; + // Expand macro expansion expression at caller side only + if (!atCallerSide && isa(defaultValue)) { + options |= TypeCheckExprFlags::DisableMacroExpansions; + } + // First, let's try to type-check default expression using // archetypes, which guarantees that it would work for any // substitution of the generic parameter (if they are involved). - if (auto result = typeCheckExpression(defaultExprTarget)) { + if (auto result = typeCheckExpression(defaultExprTarget, options)) { defaultValue = result->getAsExpr(); return defaultValue->getType(); } diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 80208bd112e..b4031ab6165 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1132,8 +1132,11 @@ Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator, auto *initExpr = param->getStructuralDefaultExpr(); assert(initExpr); - // Prohibit default argument that is a non-built-in macro to avoid confusion. - if (isa(initExpr)) { + // Prohibit default argument that is a non-built-in macro to avoid confusion, + // unless the experimental feature flag is set. + auto isMacroExpansionExpr = isa(initExpr); + if (isMacroExpansionExpr && + !ctx.LangOpts.hasFeature(Feature::ExpressionMacroDefaultArguments)) { ctx.Diags.diagnose(initExpr->getLoc(), diag::macro_as_default_argument); return new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx)); } @@ -1148,10 +1151,15 @@ Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator, assert(dc); if (!TypeChecker::typeCheckParameterDefault(initExpr, dc, paramTy, - param->isAutoClosure())) { + param->isAutoClosure(), + /*atCallerSide=*/false)) { return new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx)); } + // Expression macro default arguments are checked at caller side + if (isMacroExpansionExpr) + return initExpr; + // Walk the checked initializer and contextualize any closures // we saw there. TypeChecker::contextualizeInitializer(dc, initExpr); @@ -1545,6 +1553,7 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) { case SourceFileKind::SIL: case SourceFileKind::Interface: return; + case SourceFileKind::DefaultArgument: case SourceFileKind::Library: case SourceFileKind::Main: case SourceFileKind::MacroExpansion: @@ -2608,6 +2617,7 @@ public: case SourceFileKind::Interface: case SourceFileKind::SIL: return; + case SourceFileKind::DefaultArgument: case SourceFileKind::Main: case SourceFileKind::Library: case SourceFileKind::MacroExpansion: @@ -2630,6 +2640,7 @@ public: case SourceFileKind::Interface: case SourceFileKind::SIL: return; + case SourceFileKind::DefaultArgument: case SourceFileKind::Library: case SourceFileKind::MacroExpansion: break; diff --git a/lib/Sema/TypeCheckEffects.cpp b/lib/Sema/TypeCheckEffects.cpp index 8c3af3bba56..6316acdfc85 100644 --- a/lib/Sema/TypeCheckEffects.cpp +++ b/lib/Sema/TypeCheckEffects.cpp @@ -2196,6 +2196,10 @@ public: return getContextForPatternBinding(binding); } + static Context forDefaultArgument(DeclContext *dc) { + return Context(Kind::DefaultArgument, dc); + } + static Context forEnumElementInitializer(EnumElementDecl *elt) { return Context(Kind::EnumElementInitializer, elt); } @@ -3697,6 +3701,14 @@ void TypeChecker::checkInitializerEffects(Initializer *initCtx, init->walk(LocalFunctionEffectsChecker()); } +void TypeChecker::checkCallerSideDefaultArgumentEffects(DeclContext *initCtx, + Expr *init) { + auto &ctx = initCtx->getASTContext(); + CheckEffectsCoverage checker(ctx, Context::forDefaultArgument(initCtx)); + init->walk(checker); + init->walk(LocalFunctionEffectsChecker()); +} + /// Check the correctness of effects within the given enum /// element's raw value expression. /// diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index 64c042f926b..b7faf280126 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -710,8 +710,60 @@ Expr *TypeChecker::foldSequence(SequenceExpr *expr, DeclContext *dc) { return Result; } +static SourceFile *createDefaultArgumentSourceFile(StringRef macroExpression, + SourceLoc insertionPoint, + ASTNode target, + DeclContext *dc) { + ASTContext &ctx = dc->getASTContext(); + SourceManager &sourceMgr = ctx.SourceMgr; + + llvm::SmallString<256> builder; + unsigned line, column; + std::tie(line, column) = + sourceMgr.getPresumedLineAndColumnForLoc(insertionPoint); + auto file = dc->getOutermostParentSourceFile()->getFilename(); + + // find a way to pass the file:line:column to macro expansion + // so that we can share same buffer for the same default argument + builder.append(line - 1, '\n'); + builder.append(column - 1, ' '); + builder.append(macroExpression); + + std::unique_ptr buffer; + buffer = llvm::MemoryBuffer::getMemBufferCopy(builder.str(), file); + + // Dump default argument to standard output, if requested. + if (ctx.LangOpts.DumpMacroExpansions) { + llvm::errs() << buffer->getBufferIdentifier() + << "\n------------------------------\n" + << buffer->getBuffer() + << "\n------------------------------\n"; + } + + // Create a new source buffer with the contents of the default argument + unsigned macroBufferID = sourceMgr.addNewSourceBuffer(std::move(buffer)); + auto macroBufferRange = sourceMgr.getRangeForBuffer(macroBufferID); + GeneratedSourceInfo sourceInfo{GeneratedSourceInfo::DefaultArgument, + {insertionPoint, 0}, + macroBufferRange, + target.getOpaqueValue(), + dc, + nullptr}; + sourceMgr.setGeneratedSourceInfo(macroBufferID, sourceInfo); + + // Create a source file to hold the macro buffer. This is automatically + // registered with the enclosing module. + auto sourceFile = new (ctx) SourceFile( + *dc->getParentModule(), SourceFileKind::DefaultArgument, macroBufferID, + /*parsingOpts=*/{}, /*isPrimary=*/false); + sourceFile->setImports(dc->getParentSourceFile()->getImports()); + return sourceFile; +} + static Expr *synthesizeCallerSideDefault(const ParamDecl *param, - SourceLoc loc) { + DefaultArgumentExpr *defaultExpr, + DeclContext *dc) { + SourceLoc loc = defaultExpr->getLoc(); auto &ctx = param->getASTContext(); switch (param->getDefaultArgumentKind()) { #define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ @@ -721,6 +773,26 @@ static Expr *synthesizeCallerSideDefault(const ParamDecl *param, /*implicit=*/true); #include "swift/AST/MagicIdentifierKinds.def" + case DefaultArgumentKind::ExpressionMacro: { + // FIXME: ApolloZhu what is macro has parameters that references + // other things in decl context? + SmallString<128> scratch; + const StringRef text = param->getDefaultValueStringRepresentation(scratch); + SourceFile *defaultArgSourceFile = + createDefaultArgumentSourceFile(text, loc, defaultExpr, dc); + auto topLevelItems = defaultArgSourceFile->getTopLevelItems(); + for (auto item : topLevelItems) { + if (auto *expr = item.dyn_cast()) + if (auto *callerSideMacroExpansionExpr = + dyn_cast(expr)) { + callerSideMacroExpansionExpr->setImplicit(); + return callerSideMacroExpansionExpr; + } + } + llvm_unreachable("default argument source file missing caller side macro " + "expansion expression"); + } + case DefaultArgumentKind::NilLiteral: return new (ctx) NilLiteralExpr(loc, /*Implicit=*/true); break; @@ -750,15 +822,15 @@ Expr *CallerSideDefaultArgExprRequest::evaluate( auto paramTy = defaultExpr->getType(); // Re-create the default argument using the location info of the call site. - auto *initExpr = - synthesizeCallerSideDefault(param, defaultExpr->getLoc()); auto *dc = defaultExpr->ContextOrCallerSideExpr.get(); + auto *initExpr = synthesizeCallerSideDefault(param, defaultExpr, dc); assert(dc && "Expected a DeclContext before type-checking caller-side arg"); auto &ctx = param->getASTContext(); DiagnosticTransaction transaction(ctx.Diags); if (!TypeChecker::typeCheckParameterDefault(initExpr, dc, paramTy, - param->isAutoClosure())) { + param->isAutoClosure(), + /*atCallerSide=*/true)) { if (param->hasDefaultExpr()) { // HACK: If we were unable to type-check the default argument in context, // then retry by type-checking it within the parameter decl, which should @@ -770,6 +842,10 @@ Expr *CallerSideDefaultArgExprRequest::evaluate( } return new (ctx) ErrorExpr(initExpr->getSourceRange(), paramTy); } + if (param->getDefaultArgumentKind() == DefaultArgumentKind::ExpressionMacro) { + TypeChecker::contextualizeCallSideDefaultArgument(dc, initExpr); + TypeChecker::checkCallerSideDefaultArgumentEffects(dc, initExpr); + } return initExpr; } diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 96ec5852760..4e8abdebbdf 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -119,7 +119,12 @@ namespace { // Caller-side default arguments need their @autoclosures checked. if (auto *DAE = dyn_cast(E)) - if (DAE->isCallerSide() && DAE->getParamDecl()->isAutoClosure()) + if (DAE->isCallerSide() && + (DAE->getParamDecl()->isAutoClosure() || + (DAE->getParamDecl()->getDefaultArgumentKind() == + DefaultArgumentKind::ExpressionMacro && + ParentDC->getASTContext().LangOpts.hasFeature( + Feature::ExpressionMacroDefaultArguments)))) DAE->getCallerSideDefaultExpr()->walk(*this); // Macro expansion expressions require a DeclContext as well. @@ -206,6 +211,12 @@ void TypeChecker::contextualizeInitializer(Initializer *DC, Expr *E) { E->walk(CC); } +void TypeChecker::contextualizeCallSideDefaultArgument(DeclContext *DC, + Expr *E) { + ContextualizeClosuresAndMacros CC(DC); + E->walk(CC); +} + void TypeChecker::contextualizeTopLevelCode(TopLevelCodeDecl *TLCD) { ContextualizeClosuresAndMacros CC(TLCD); if (auto *body = TLCD->getBody()) diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index b692576b832..7c26d04c6dc 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -372,6 +372,7 @@ void swift::performWholeModuleTypeChecking(SourceFile &SF) { FrontendStatsTracer tracer(Ctx.Stats, "perform-whole-module-type-checking"); switch (SF.Kind) { + case SourceFileKind::DefaultArgument: case SourceFileKind::Library: case SourceFileKind::Main: case SourceFileKind::MacroExpansion: @@ -418,6 +419,7 @@ void swift::loadDerivativeConfigurations(SourceFile &SF) { }; switch (SF.Kind) { + case SourceFileKind::DefaultArgument: case SourceFileKind::Library: case SourceFileKind::MacroExpansion: case SourceFileKind::Main: { diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index bd510097241..09534115456 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -467,7 +467,8 @@ bool typeCheckClosureBody(ClosureExpr *closure); bool typeCheckTapBody(TapExpr *expr, DeclContext *DC); Type typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, - Type paramType, bool isAutoClosure); + Type paramType, bool isAutoClosure, + bool atCallerSide); void typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD); @@ -760,6 +761,7 @@ void checkPatternBindingCaptures(IterableDeclContext *DC); /// Change the context of closures in the given initializer /// expression to the given context. void contextualizeInitializer(Initializer *DC, Expr *init); +void contextualizeCallSideDefaultArgument(DeclContext *DC, Expr *init); void contextualizeTopLevelCode(TopLevelCodeDecl *TLCD); /// Retrieve the default type for the given protocol. @@ -1135,6 +1137,7 @@ void checkForForbiddenPrefix(ASTContext &C, DeclBaseName Name); void checkTopLevelEffects(TopLevelCodeDecl *D); void checkFunctionEffects(AbstractFunctionDecl *D); void checkInitializerEffects(Initializer *I, Expr *E); +void checkCallerSideDefaultArgumentEffects(DeclContext *I, Expr *E); void checkEnumElementEffects(EnumElementDecl *D, Expr *expr); void checkPropertyWrapperEffects(PatternBindingDecl *binding, Expr *expr); diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index d1e6dddd058..ef805489871 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -452,6 +452,7 @@ getActualDefaultArgKind(uint8_t raw) { CASE(EmptyArray) CASE(EmptyDictionary) CASE(StoredProperty) + CASE(ExpressionMacro) #undef CASE } return llvm::None; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 4c3a78f91fc..825bda6e2d3 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -537,6 +537,7 @@ enum class DefaultArgumentKind : uint8_t { EmptyArray, EmptyDictionary, StoredProperty, + ExpressionMacro, }; using DefaultArgumentField = BCFixed<4>; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index fe29cfefb8b..25c46f16acc 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1415,6 +1415,7 @@ static uint8_t getRawStableDefaultArgumentKind(swift::DefaultArgumentKind kind) CASE(EmptyArray) CASE(EmptyDictionary) CASE(StoredProperty) + CASE(ExpressionMacro) #undef CASE } @@ -4402,12 +4403,14 @@ public: swift::DefaultArgumentKind argKind = param->getDefaultArgumentKind(); if (argKind == swift::DefaultArgumentKind::Normal || - argKind == swift::DefaultArgumentKind::StoredProperty) { + argKind == swift::DefaultArgumentKind::StoredProperty || + argKind == swift::DefaultArgumentKind::ExpressionMacro) { defaultArgumentText = param->getDefaultValueStringRepresentation(scratch); // Serialize the type of the default expression (if any). - defaultExprType = param->getTypeOfDefaultExpr(); + if (!param->hasCallerSideDefaultExpr()) + defaultExprType = param->getTypeOfDefaultExpr(); } auto isolation = param->getInitializerIsolation(); diff --git a/test/Macros/Inputs/syntax_macro_definitions.swift b/test/Macros/Inputs/syntax_macro_definitions.swift index 8287a943252..f216661ce70 100644 --- a/test/Macros/Inputs/syntax_macro_definitions.swift +++ b/test/Macros/Inputs/syntax_macro_definitions.swift @@ -1986,6 +1986,74 @@ public struct NestedMagicLiteralMacro: ExpressionMacro { } } +public struct NativeFileIDMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + return context.location( + of: node, at: .afterLeadingTrivia, filePathMode: .fileID + )!.file + } +} + +public struct NativeFilePathMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + return context.location( + of: node, at: .afterLeadingTrivia, filePathMode: .filePath + )!.file + } +} + +public struct NativeLineMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + return context.location(of: node)!.line + } +} + +public struct NativeColumnMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + return context.location(of: node)!.column + } +} + +public struct ClosureCallerMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + let location = context.location(of: node)! + return #""" + ClosureCaller({ (value, then) in + #sourceLocation(file: \#(location.file), line: \#(location.line)) + print("\(value)@\(\#(location.file))#\(\#(location.line))") + then() + #sourceLocation() + }) + """# + } +} + +public struct FirstArgumentConcatStringMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + """ + \(node.arguments.first!.expression) + " world" + """ + } +} + public struct InvalidIfExprMacro: MemberMacro { public static func expansion( of node: AttributeSyntax, diff --git a/test/Macros/Inputs/with_macro_default_arg_interface.swift b/test/Macros/Inputs/with_macro_default_arg_interface.swift new file mode 100644 index 00000000000..0997efcb02a --- /dev/null +++ b/test/Macros/Inputs/with_macro_default_arg_interface.swift @@ -0,0 +1,49 @@ +@freestanding(expression) +public macro FileID() -> T = #externalMacro( + module: "MacroDefinition", type: "NativeFileIDMacro" +) + +@freestanding(expression) +public macro HasParam(_ param: String) -> String = #externalMacro( + module: "MacroDefinition", type: "FirstArgumentConcatStringMacro" +) + +@freestanding(expression) +public macro MakeClosureCaller() -> ClosureCaller = #externalMacro( + module: "MacroDefinition", type: "ClosureCallerMacro" +) + +public func printCurrentFileDefinedInAnotherModuleInterface( + file: String = #FileID +) { + print(file) +} + +public struct ClosureCaller { + private let callback: @convention(thin) (Any, () -> Void) -> Void + + public init(_ callback: @convention(thin) (Any, () -> Void) -> Void) { + self.callback = callback + } + + public func callAsFunction(context: Any, then: () -> Void = {}) { + callback(context, then) + } +} + +public let shadowed = "hello" + +public func testParameterUseVariableFromOriginalDeclContext( + param: String = #HasParam(shadowed) +) { + print(param) +} + +@resultBuilder +public enum ClosureCallerBuilder { + public static func buildBlock( + closureCaller: ClosureCaller = #MakeClosureCaller + ) -> ClosureCaller { + closureCaller + } +} diff --git a/test/Macros/Inputs/with_macro_default_arg_module.swift b/test/Macros/Inputs/with_macro_default_arg_module.swift new file mode 100644 index 00000000000..fe2f5cba5c4 --- /dev/null +++ b/test/Macros/Inputs/with_macro_default_arg_module.swift @@ -0,0 +1,8 @@ +@freestanding(expression) +public macro Line() -> T = #externalMacro( + module: "MacroDefinition", type: "NativeLineMacro" +) + +public func printCurrentLineDefinedAtAnotherModule(line: Int = #Line) { + print(line) +} diff --git a/test/Macros/Inputs/with_macro_default_arg_same_module.swift b/test/Macros/Inputs/with_macro_default_arg_same_module.swift new file mode 100644 index 00000000000..4d236f366c6 --- /dev/null +++ b/test/Macros/Inputs/with_macro_default_arg_same_module.swift @@ -0,0 +1,29 @@ +import WithMacroDefaultArg +import WithMacroDefaultArgInterface + +@freestanding(expression) +macro MagicLine() -> Int = #externalMacro( + module: "MacroDefinition", type: "MagicLineMacro" +) + +@freestanding(expression) +macro Column() -> T = #externalMacro( + module: "MacroDefinition", type: "NativeColumnMacro" +) + +@freestanding(expression) +macro FilePath() -> T = #externalMacro( + module: "MacroDefinition", type: "NativeFilePathMacro" +) + +func printAnotherFileName(file: String = (#FileID)) { + print(file) +} + +func printCurrentFileDefinedAtAnotherFile(file: String = #FileID) { + print(file) +} + +func printCurrentLineDefinedAtAnotherFile(line: Int = #Line) { + print(line) +} diff --git a/test/Macros/macro_default_argument_enabled.swift b/test/Macros/macro_default_argument_enabled.swift new file mode 100644 index 00000000000..f2af11af13b --- /dev/null +++ b/test/Macros/macro_default_argument_enabled.swift @@ -0,0 +1,138 @@ +// REQUIRES: swift_swift_parser + +// RUN: %empty-directory(%t) + +// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath + +// RUN: %target-build-swift-dylib(%t/%target-library-name(WithMacroDefaultArg)) %S/Inputs/with_macro_default_arg_module.swift -module-name WithMacroDefaultArg -emit-module -emit-module-path %t/WithMacroDefaultArg.swiftmodule -load-plugin-library %t/%target-library-name(MacroDefinition) -enable-experimental-feature ExpressionMacroDefaultArguments -I %t -swift-version 5 +// RUN: %target-codesign %t/%target-library-name(WithMacroDefaultArg) + +// RUN: %target-build-swift-dylib(%t/%target-library-name(WithMacroDefaultArgInterface)) %S/Inputs/with_macro_default_arg_interface.swift -module-name WithMacroDefaultArgInterface -enable-library-evolution -emit-module-interface-path %t/WithMacroDefaultArgInterface.swiftinterface -load-plugin-library %t/%target-library-name(MacroDefinition) -enable-experimental-feature ExpressionMacroDefaultArguments -I %t -swift-version 5 +// RUN: %target-codesign %t/%target-library-name(WithMacroDefaultArgInterface) + +// RUN: %target-build-swift -enable-experimental-feature ExpressionMacroDefaultArguments -swift-version 5 -load-plugin-library %t/%target-library-name(MacroDefinition) %S/Inputs/with_macro_default_arg_same_module.swift %s -o %t/main -module-name MacroUser -emit-tbd -emit-tbd-path %t/MacroUser.tbd -I %t -L %t -lWithMacroDefaultArg -lWithMacroDefaultArgInterface +// RUN: %target-codesign %t/main +// RUN: %target-run %t/main | %FileCheck %s + +import WithMacroDefaultArg +import WithMacroDefaultArgInterface + +struct SourceLocation: CustomStringConvertible { + let line: Int + let column: Int + + var description: String { + "\(line):\(column)" + } +} + +func partOfDefaultArgumentOkay( + // CHECK: [[# @LINE + 1]]:59 + location: SourceLocation = .init(line: #Line, column: #Column) +) { + print(location) +} + +// CHECK: [[# @LINE + 1]] +func parenthesizedExpansionAtDeclOkay(line: Int = (#Line)) { + print(line) +} + +func asDefaultArgument(line: Int = #Line) { + print(line) +} + +func asDefaultArgumentExpandingToBuiltInLine(line: Int = #MagicLine) { + print(line) +} + +@resultBuilder +enum SourceLocationBuilder { + static func buildExpression( + _ expression: T, line: Int = #Line, column: Int = #Column + ) -> SourceLocation { + SourceLocation(line: line, column: column) + } + + static func buildBlock(_ location: SourceLocation) -> SourceLocation { + location + } +} + +func build(@SourceLocationBuilder body: () -> SourceLocation) -> SourceLocation { + body() +} + +let result = build { + // CHECK: [[# @LINE + 1]]:5 + 0 +} + +func sameFileID(builtIn: String = #fileID, expressionMacro: String = #FileID) { + print(builtIn == expressionMacro) +} + +func sameFilePath(builtIn: String = #filePath, expressionMacro: String = #FilePath) { + print(builtIn == expressionMacro) +} + +func sameLine(builtIn: Int = #line, expressionMacro: Int = #Line) { + print(builtIn == expressionMacro) +} + +func sameColumn(builtIn: Int = #column, expressionMacro: Int = #Column) { + print(builtIn == expressionMacro) +} + +func buildPrinter( + @ClosureCallerBuilder makeCaller: () -> ClosureCaller +) -> ClosureCaller { + makeCaller() +} + +// CHECK: macro@MacroUser/macro_default_argument_enabled.swift#[[# @LINE + 1]] +let printWithFileLine = buildPrinter { } + +@main struct Main { + static func main() { + partOfDefaultArgumentOkay() + parenthesizedExpansionAtDeclOkay() + print(result) + printWithFileLine(context: "macro") + + do { + let shadowed: Int = 1 + // CHECK: hello world + testParameterUseVariableFromOriginalDeclContext() + } + + do { + let shadowed: String = "not this" + // CHECK: hello world + testParameterUseVariableFromOriginalDeclContext() + } + + // CHECK: [[# @LINE + 1]] + asDefaultArgument() + // CHECK: [[# @LINE + 1]] + asDefaultArgumentExpandingToBuiltInLine() + // CHECK: [[# @LINE + 1]] + printCurrentLineDefinedAtAnotherFile() + // CHECK: [[# @LINE + 1]] + printCurrentLineDefinedAtAnotherModule() + // CHECK: MacroUser/with_macro_default_arg_same_module.swift + printAnotherFileName() + // CHECK: MacroUser/macro_default_argument_enabled.swift + printCurrentFileDefinedAtAnotherFile() + // CHECK: MacroUser/macro_default_argument_enabled.swift + printCurrentFileDefinedInAnotherModuleInterface() + // CHECK: true + sameFileID() + // CHECK: true + sameFilePath() + // CHECK: true + sameLine() + // CHECK: true + sameColumn() + } +}