Skip to content

Commit

Permalink
Merge pull request #26518 from burmako/quasiquotes
Browse files Browse the repository at this point in the history
Experimental prototype of Swift quasiquotes
  • Loading branch information
Eugene Burmako committed Aug 8, 2019
2 parents 341d316 + a14554d commit 28eb536
Show file tree
Hide file tree
Showing 42 changed files with 2,255 additions and 6 deletions.
16 changes: 16 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,22 @@ class ASTContext final {
/// has been imported. Otherwise, this returns null.
StructDecl *getTensorDataTypeDecl() const;

/// Retrieve the decl for the Quote module iff it has been imported.
/// Otherwise, this returns null.
ModuleDecl *getQuoteModule() const;

/// Retrieve the decl for Quote.Tree iff the Quote module has been imported.
/// Otherwise, this returns null.
ProtocolDecl *getTreeDecl() const;

/// Retrieve the decl for Quote.Quote iff the Quote module has been imported.
/// Otherwise, this returns null.
ClassDecl *getQuoteDecl() const;

/// Retrieve the decl for Quote.FunctionQuoteN iff the Quote module has been
/// imported. Otherwise, this returns null.
ClassDecl *getFunctionQuoteDecl(unsigned n) const;

/// Retrieve the type Swift.Never.
CanType getNeverType() const;

Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ TYPE_ATTR(escaping)
TYPE_ATTR(differentiable)
TYPE_ATTR(autodiff)
TYPE_ATTR(nondiff)
TYPE_ATTR(quoted)

// SIL-specific attributes
TYPE_ATTR(block_storage)
Expand Down Expand Up @@ -439,6 +440,10 @@ SIMPLE_DECL_ATTR(IBSegueAction, IBSegueAction,
OnFunc,
95)

DECL_ATTR(quoted, Quoted,
OnFunc,
96)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
61 changes: 61 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,67 @@ class ProjectedValuePropertyAttr : public DeclAttribute {
}
};

/// Attribute that asks the compiler to generate a function that returns a
/// quoted representation of the attributed declaration.
///
/// @quoted
/// func identity(_ x: Int) -> Int {
/// return x;
/// }
///
/// The generated function, called "quote decl", looks along the following lines
/// (the exact representation may change over time since quasiquotes are an
/// experimental feature, e.g. the name may end up being mangled as per #13):
///
/// func _quotedIdentity() -> Tree {
/// return #quote{
/// func identity(_ x: Int) -> Int {
/// return x;
/// }
/// }
/// }
///
/// Quote decls are not supposed to be called manually. Instead, it is expected
/// that #quote(...) will be used to obtain representations of @quoted
/// declarations, by synthesizing calls to quote decls. This way users don't
/// have to know the details of name mangling in the presence of overloads etc:
///
/// #quote(identity)
///
/// Unquote(
/// Name(
/// "foo",
/// "s:4main3fooyS2fF",
/// FunctionType(
/// [],
/// [TypeName("Int", "s:Si")],
/// TypeName("Int", "s:Si"))),
/// { () -> Tree in quotedIdentity() },
/// FunctionType(
/// [],
/// [TypeName("Int", "s:Si")],
/// TypeName("Int", "s:Si")))
class QuotedAttr final : public DeclAttribute {
FuncDecl *QuoteDecl;

explicit QuotedAttr(FuncDecl *quoteDecl, SourceLoc atLoc, SourceRange range,
bool implicit);

public:
FuncDecl *getQuoteDecl() const { return QuoteDecl; }
void setQuoteDecl(FuncDecl *quoteDecl) { QuoteDecl = quoteDecl; }

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_Quoted;
}

static QuotedAttr *create(ASTContext &context, SourceLoc atLoc,
SourceRange range, bool implicit);

static QuotedAttr *create(ASTContext &context, FuncDecl *quoteDecl,
SourceLoc atLoc, SourceRange range, bool implicit);
};

/// Attributes that may be applied to declarations.
class DeclAttributes {
/// Linked list of declaration attributes.
Expand Down
5 changes: 3 additions & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6279,8 +6279,9 @@ AbstractStorageDecl::AccessorRecord::getAccessor(AccessorKind kind) const {

/// This represents a 'case' declaration in an 'enum', which may declare
/// one or more individual comma-separated EnumElementDecls.
class EnumCaseDecl final : public Decl,
private llvm::TrailingObjects<EnumCaseDecl, EnumElementDecl *> {
class EnumCaseDecl final
: public Decl,
private llvm::TrailingObjects<EnumCaseDecl, EnumElementDecl *> {
friend TrailingObjects;
SourceLoc CaseLoc;

Expand Down
29 changes: 29 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1221,6 +1221,30 @@ ERROR(legacy_object_literal,none,
"'%select{|[}0#%1(...)%select{|#]}0' has been renamed to '#%2(...)'",
(bool, StringRef, StringRef))

// Quote literal expressions.
ERROR(expr_quote_enable_experimental_quasiquotes,PointsToFirstBadToken,
"'#quote' is unsupported; pass -enable-experimental-quasiquotes to "
"enable support for quasiquotes", ())
ERROR(expr_quote_expected_lparen_or_lbrace,PointsToFirstBadToken,
"expected '(' or '}' following '#quote'", ())
ERROR(expr_quote_expected_expr,PointsToFirstBadToken,
"expected expression within '#quote(...)'", ())
ERROR(expr_quote_expected_closure,PointsToFirstBadToken,
"expected closure within '#quote{...}'", ())
ERROR(expr_quote_expected_rparen,PointsToFirstBadToken,
"expected ')' to complete '#quote' expression", ())

// Unquote expressions.
ERROR(expr_unquote_enable_experimental_quasiquotes,PointsToFirstBadToken,
"'#unquote' is unsupported; pass -enable-experimental-quasiquotes to "
"enable support for quasiquotes", ())
ERROR(expr_unquote_expected_lparen,PointsToFirstBadToken,
"expected '(' following '#unquote'", ())
ERROR(expr_unquote_expected_expr,PointsToFirstBadToken,
"expected expression within '#unquote(...)'", ())
ERROR(expr_unquote_expected_rparen,PointsToFirstBadToken,
"expected ')' to complete '#unquote' expression", ())

// Unknown pound expression.
ERROR(unknown_pound_expr,none,
"use of unknown directive '#%0'", (StringRef))
Expand Down Expand Up @@ -1580,6 +1604,11 @@ ERROR(sil_inst_autodiff_expected_associated_function_kind_attr,PointsToFirstBadT
ERROR(sil_inst_autodiff_expected_function_type_operand,PointsToFirstBadToken,
"expected an operand of a function type", ())

// Quoted attribute.
ERROR(attr_quoted_enable_experimental_quasiquotes,PointsToFirstBadToken,
"'@quoted' is unsupported; pass -enable-experimental-quasiquotes to "
"enable support for quasiquotes", ())

//------------------------------------------------------------------------------
// MARK: Generics parsing diagnostics
//------------------------------------------------------------------------------
Expand Down
16 changes: 16 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3078,6 +3078,22 @@ ERROR(interpolation_broken_proto,none,
ERROR(object_literal_broken_proto,none,
"object literal protocol is broken", ())

ERROR(quote_literal_no_function_quote_class,none,
"cannot expand quote literal: Quote.FunctionQuote%0 not found", (int))
ERROR(quote_literal_no_quote_class,none,
"cannot expand quote literal: Quote.Quote not found", ())
ERROR(quote_literal_no_quote_module,none,
"cannot expand quote literal: Quote not imported", ())
ERROR(quote_literal_no_tree_proto,none,
"cannot expand quote literal: Quote.Tree not found", ())
WARNING(quote_literal_unsupported_brief,none,
"unsupported code snippet", ())
WARNING(quote_literal_unsupported_detailed,none,
"unsupported code snippet\n%0", (StringRef))

ERROR(unquote_wrong_type,none,
"can only unquote expressions of type Quote and FunctionQuoteN", ())

ERROR(discard_expr_outside_of_assignment,none,
"'_' can only appear in a pattern or on the left side of an assignment",
())
Expand Down
98 changes: 98 additions & 0 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,104 @@ class ObjectLiteralExpr final
}
};

/// QuoteLiteralExpr - An expression that produces an abstract syntax tree
/// that represents its argument.
///
/// Examples:
/// #quote(foo + bar)
/// #quote{ foo + bar }
class QuoteLiteralExpr : public LiteralExpr {
private:
SourceLoc PoundLoc;
Expr *SubExpr;
Expr *SemanticExpr;

public:
static QuoteLiteralExpr *create(ASTContext &ctx, SourceLoc poundLoc,
Expr *subExpr);

Expr *getSubExpr() const { return SubExpr; }
void setSubExpr(Expr *subExpr) { SubExpr = subExpr; }

Expr *getSemanticExpr() const { return SemanticExpr; }
void setSemanticExpr(Expr *semanticExpr) { SemanticExpr = semanticExpr; }

SourceLoc getSourceLoc() const { return PoundLoc; }
SourceRange getSourceRange() const {
return SourceRange(PoundLoc, SubExpr->getEndLoc());
}

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::QuoteLiteral;
}

private:
explicit QuoteLiteralExpr(SourceLoc poundLoc, Expr *subExpr);
};

/// UnquoteExpr - An expression that shields an expression from being quoted
/// when used inside #quote(...)
///
/// Examples:
/// #quote(foo + #unquote(bar))
class UnquoteExpr : public Expr {
private:
SourceLoc PoundLoc;
Expr *SubExpr;

public:
static UnquoteExpr *create(ASTContext &ctx, SourceLoc poundLoc,
Expr *subExpr);

Expr *getSubExpr() const { return SubExpr; }
void setSubExpr(Expr *subExpr) { SubExpr = subExpr; }

SourceLoc getSourceLoc() const { return PoundLoc; }
SourceRange getSourceRange() const {
return SourceRange(PoundLoc, SubExpr->getEndLoc());
}

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::Unquote;
}

private:
explicit UnquoteExpr(SourceLoc poundLoc, Expr *subExpr);
};

/// DeclQuoteExpr - An expression that produces an abstract syntax tree
/// that represents the underlying declaration.
///
/// This expression does not have Swift syntax and is only used to represent
/// bodies of quote decls generated for @quoted declarations. See documentation
/// for QuoteAttr for details.
class DeclQuoteExpr : public Expr {
private:
ValueDecl *QuotedDecl;
Expr *SemanticExpr;

public:
static DeclQuoteExpr *create(ASTContext &ctx, ValueDecl *quotedDecl);

ValueDecl *getQuotedDecl() const { return QuotedDecl; }
void setQuotedDecl(ValueDecl *quotedDecl) { QuotedDecl = quotedDecl; }

Expr *getSemanticExpr() const { return SemanticExpr; }
void setSemanticExpr(Expr *semanticExpr) { SemanticExpr = semanticExpr; }

SourceLoc getSourceLoc() const { return SourceLoc(); }
SourceRange getSourceRange() const {
return SourceRange(SourceLoc(), SourceLoc());
}

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::DeclQuote;
}

private:
explicit DeclQuoteExpr(ValueDecl *quotedDecl);
};

/// DiscardAssignmentExpr - A '_' in the left-hand side of an assignment, which
/// discards the corresponding tuple element on the right-hand side.
class DiscardAssignmentExpr : public Expr {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/ExprNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ ABSTRACT_EXPR(Literal, Expr)
LITERAL_EXPR(StringLiteral, LiteralExpr)
LITERAL_EXPR(InterpolatedStringLiteral, LiteralExpr)
LITERAL_EXPR(ObjectLiteral, LiteralExpr)
LITERAL_EXPR(QuoteLiteral, LiteralExpr)
LITERAL_EXPR(MagicIdentifierLiteral, LiteralExpr)
EXPR_RANGE(Literal, NilLiteral, MagicIdentifierLiteral)
EXPR(DiscardAssignment, Expr)
Expand Down Expand Up @@ -194,6 +195,8 @@ EXPR(ObjCSelector, Expr)
EXPR(KeyPath, Expr)
UNCHECKED_EXPR(KeyPathDot, Expr)
EXPR(Tap, Expr)
EXPR(Unquote, Expr)
EXPR(DeclQuote, Expr)
LAST_EXPR(Tap)

#undef EXPR_RANGE
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ IDENTIFIER_(nsError)
// Custom string interpolation type used by os log APIs.
IDENTIFIER(OSLogMessage)

// Module that supports #quote(...) literals.
IDENTIFIER(Quote)

#undef IDENTIFIER
#undef IDENTIFIER_
#undef IDENTIFIER_WITH_NAME
3 changes: 3 additions & 0 deletions include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByUnicodeScalarLiteral, "UnicodeScala
EXPRESSIBLE_BY_LITERAL_PROTOCOL_(ExpressibleByColorLiteral, "_ColorLiteralType", true)
EXPRESSIBLE_BY_LITERAL_PROTOCOL_(ExpressibleByImageLiteral, "_ImageLiteralType", true)
EXPRESSIBLE_BY_LITERAL_PROTOCOL_(ExpressibleByFileReferenceLiteral, "_FileReferenceLiteralType", true)
// TODO(TF-735): Implement ExpressibleByQuoteLiteral.
// EXPRESSIBLE_BY_LITERAL_PROTOCOL_(ExpressibleByQuoteLiteral, "_QuoteLiteralType", true)
PROTOCOL(Expression)

BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_(ExpressibleByBuiltinBooleanLiteral)
BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_(ExpressibleByBuiltinExtendedGraphemeClusterLiteral)
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ namespace swift {
/// set to true.
bool ExperimentalDependenciesIncludeIntrafileOnes = false;

/// Whether to enable #quote, #unquote and @quoted.
bool EnableExperimentalQuasiquotes = false;

/// Sets the target we are building for and updates platform conditions
/// to match.
///
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,11 @@ def disable_bridging_pch : Flag<["-"], "disable-bridging-pch">,
Flags<[HelpHidden]>,
HelpText<"Disable automatic generation of bridging PCH files">;

// Experimental feature options
def enable_experimental_quasiquotes : Flag<["-"], "enable-experimental-quasiquotes">,
Flags<[FrontendOption]>,
HelpText<"Experimental work-in-progress implementation of quasiquotes">;

// Diagnostic control options
def suppress_warnings : Flag<["-"], "suppress-warnings">,
Flags<[FrontendOption]>,
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,9 @@ class Parser {
ParserResult<TransposingAttr> parseTransposingAttribute(SourceLoc AtLoc,
SourceLoc Loc);

/// Parse the @quoted attribute.
ParserResult<QuotedAttr> parseQuotedAttribute(SourceLoc AtLoc, SourceLoc Loc);

/// Parse a specific attribute.
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc);

Expand Down Expand Up @@ -1463,6 +1466,8 @@ class Parser {
/// \param LK The literal kind as determined by the first token.
ParserResult<Expr> parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LK,
bool isExprBasic);
ParserResult<Expr> parseExprQuoteLiteral();
ParserResult<Expr> parseExprUnquote();
ParserResult<Expr> parseExprCallSuffix(ParserResult<Expr> fn,
bool isExprBasic);
ParserResult<Expr> parseExprCollection();
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,10 @@ namespace decls_block {
TypeIDField // type referenced by this custom attribute
>;

using QuotedDeclAttrLayout = BCRecordLayout<Quoted_DECL_ATTR,
BCFixed<1>, // implicit flag
DeclIDField // quote decl
>;
}

/// Returns the encoding kind for the given decl.
Expand Down
Loading

0 comments on commit 28eb536

Please sign in to comment.