diff a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -26,17 +26,19 @@
 package com.sun.tools.javac.comp;
 
 import java.util.*;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.stream.Stream;
 
 import javax.lang.model.element.ElementKind;
 import javax.tools.JavaFileObject;
 
 import com.sun.source.tree.CaseTree;
 import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LambdaExpressionTree.BodyKind;
 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
 import com.sun.source.tree.MemberSelectTree;
 import com.sun.source.tree.TreeVisitor;
 import com.sun.source.util.SimpleTreeVisitor;
 import com.sun.tools.javac.code.*;
@@ -45,12 +47,15 @@
 import com.sun.tools.javac.code.Source.Feature;
 import com.sun.tools.javac.code.Symbol.*;
 import com.sun.tools.javac.code.Type.*;
 import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
 import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext;
+import com.sun.tools.javac.comp.Attr.ResultInfo;
 import com.sun.tools.javac.comp.Check.CheckContext;
+import com.sun.tools.javac.comp.Check.NestedCheckContext;
 import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
+import com.sun.tools.javac.comp.DeferredAttr.LambdaReturnScanner;
 import com.sun.tools.javac.comp.MatchBindingsComputer.MatchBindings;
 import com.sun.tools.javac.jvm.*;
 
 import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond;
 import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg;
@@ -59,10 +64,11 @@
 import com.sun.tools.javac.resources.CompilerProperties.Errors;
 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
 import com.sun.tools.javac.tree.*;
 import com.sun.tools.javac.tree.JCTree.*;
+import com.sun.tools.javac.tree.JCTree.JCLambda.ParameterKind;
 import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.DefinedBy.Api;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.JCDiagnostic.Error;
@@ -441,10 +447,23 @@
             log.useSource(prev);
         }
         return env;
     }
 
+    public <R> R runWithAttributedMethod(Env<AttrContext> env, JCMethodDecl tree, Function<JCBlock, R> attributedAction) {
+        JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile);
+        try {
+            JCBlock dupTree = (JCBlock)deferredAttr.attribSpeculative(tree.body, env, statInfo,
+                    null, DeferredAttr.AttributionMode.ATTRIB_TO_TREE,
+                    argumentAttr.withLocalCacheContext());
+            return attributedAction.apply(dupTree);
+        } finally {
+            attrRecover.doRecovery();
+            log.useSource(prevSource);
+        }
+    }
+
     private JCTree breakTree = null;
 
     private static class BreakAttr extends RuntimeException {
         static final long serialVersionUID = -6924771130405446405L;
         private transient Env<AttrContext> env;
@@ -3134,10 +3153,18 @@
                 log.error(that.pos(), Errors.UnexpectedLambda);
             }
             resultInfo = recoveryInfo;
             wrongContext = true;
         }
+        if (types.isQuoted(pt())) {
+            attribQuotedLambda(that);
+        } else {
+            attribFunctionalLambda(that, wrongContext);
+        }
+    }
+
+    void attribFunctionalLambda(JCLambda that, boolean wrongContext) {
         //create an environment for attribution of the lambda expression
         final Env<AttrContext> localEnv = lambdaEnv(that, env);
         boolean needsRecovery =
                 resultInfo.checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.CHECK;
         try {
@@ -3277,10 +3304,103 @@
                     }
                 }
             }
         }
     }
+
+    void attribQuotedLambda(JCLambda that) {
+        // create an environment for attribution of the lambda expression
+        final Env<AttrContext> localEnv = lambdaEnv(that, env);
+        try {
+            // if quoted lambda is implicit, issue error, and recover
+            if (that.paramKind == ParameterKind.IMPLICIT) {
+                log.error(that, Errors.QuotedLambdaMustBeExplicit);
+                // recovery
+                List<JCVariableDecl> params = that.params;
+                while (params.nonEmpty()) {
+                    Type argType = syms.errType;
+                    if (params.head.isImplicitlyTyped()) {
+                        setSyntheticVariableType(params.head, argType);
+                    }
+                    params = params.tail;
+                }
+            }
+            // attribute lambda parameters
+            attribStats(that.params, localEnv);
+            List<Type> explicitParamTypes = TreeInfo.types(that.params);
+
+            ListBuffer<Type> restypes = new ListBuffer<>();
+            ListBuffer<DiagnosticPosition> resPositions = new ListBuffer<>();
+            ResultInfo bodyResultInfo = localEnv.info.returnResult = unknownExprInfo;
+
+            // type-check lambda body, and capture return types
+            if (that.getBodyKind() == JCLambda.BodyKind.EXPRESSION) {
+                attribTree(that.getBody(), localEnv, bodyResultInfo);
+                restypes.add(that.getBody().type);
+            } else {
+                JCBlock body = (JCBlock)that.body;
+                if (body == breakTree &&
+                        resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) {
+                    breakTreeFound(copyEnv(localEnv));
+                }
+                attribStats(body.stats, localEnv);
+                new LambdaReturnScanner() {
+                    @Override
+                    public void visitReturn(JCReturn tree) {
+                        if (tree.expr != null) {
+                            resPositions.add(tree);
+                        }
+                        restypes.add(tree.expr == null ? syms.voidType : tree.expr.type);
+                    }
+                }.scan(body);
+            }
+
+            // check if lambda body can complete normally
+            preFlow(that);
+            flow.analyzeLambda(localEnv, that, make, false);
+
+            final Type restype;
+            if (that.getBodyKind() == BodyKind.STATEMENT) {
+                if (that.canCompleteNormally) {
+                    // a lambda that completes normally has an implicit void return
+                    restypes.add(syms.voidType);
+                }
+
+                boolean hasNonVoidReturn = restypes.toList()
+                        .stream().anyMatch(t -> t != syms.voidType);
+                boolean hasVoidReturn = restypes.toList()
+                        .stream().anyMatch(t -> t == syms.voidType);
+
+                if (hasVoidReturn && hasNonVoidReturn) {
+                    // void vs. non-void mismatch
+                    log.error(that.body, Errors.CantInferQuotedLambdaReturnType(restypes.toList()));
+                    restype = syms.errorType;
+                } else if (hasVoidReturn) {
+                    restype = syms.voidType;
+                } else {
+                    restype = condType(resPositions.toList(), restypes.toList());
+                }
+            } else {
+                restype = restypes.first();
+            }
+
+            // infer lambda return type using lub
+            if (restype.hasTag(ERROR)) {
+                // some other error occurred
+                log.error(that.body, Errors.CantInferQuotedLambdaReturnType(restypes.toList()));
+            }
+
+            // infer thrown types
+            List<Type> thrownTypes = flow.analyzeLambdaThrownTypes(localEnv, that, make);
+
+            // set up target descriptor with explicit parameter types, and inferred thrown/return types
+            that.target = new MethodType(explicitParamTypes, restype, thrownTypes, syms.methodClass);
+            result = that.type = pt();
+        } finally {
+            localEnv.info.scope.leave();
+        }
+    }
     //where
         class TargetInfo {
             Type target;
             Type descriptor;
 
