Repository: uber-go/fx (go)

Issue:
fx.Supply with interface types
Related to #673 

Let's say we have a following, pretty usual use case:
```
v := some.NewCustomIface()
//
//
//
fx.New(
  fx.Supply(v),
)
```
The type of the value `Supply` actually supplies is not the interface type of `v`, but rather its underlying type, possibly outright private in package`some`. This is pretty unfortunate and limits the use of `Supply` option greatly.


Pull request:
fx.Annotate: Refactor and support fx.Supply
This changes how we implement fx.Annotate.
Rather than generating a constructor when fx.Annotate is called,
we now build an internal `fx.annotate` type--a more featureful
version of `fx.Annotated`.

We then intercept this `fx.annotated` type,
similar to how we intercept `fx.Annotated`,
and convert it into a constructor on-demand.

To build the constructor itself,
we rely on the `annotated.Build` method.
The Build method operates by splitting annotation into two:

- parameter annotation
- result annotation

For both pieces, we turn annotations into:

    types []reflect.Type
    remap func([]reflect.Value) []reflect.Value

Where `types` specifies the type of parameters/results
for the generated function,
and `remap` turns maps function arguments/results
from/to the generated function.

            .--------------------.
            | generated function |
            |                    |
            |  +--------------+  |
    args ----->| remap params |  |
            |  +------.-------+  |
            |         |          |
            |         |          |
            |  .------v-------.  |
            |  | original  fn |  |
            |  '------.-------'  |
            |         |          |
            |         |          |
            |  +------v-------+  |
     out <-----| remap output |  |
            |  +------.-------+  |
            '--------------------'

Besides providing a cleaner implementation,
this lets us implement support for fx.Annotate in fx.Supply
with a fairly small change.

Resolves #714
Depends on #795




Patch:
diff --git a/annotated.go b/annotated.go
index b788dd2..e0cfbee 100644
--- a/annotated.go
+++ b/annotated.go
@@ -27,7 +27,6 @@ import (
 	"strings"
 
 	"go.uber.org/fx/internal/fxreflect"
-	"go.uber.org/multierr"
 )
 
 // Annotated annotates a constructor provided to Fx with additional options.
@@ -93,43 +92,6 @@ func (a Annotated) String() string {
 	return fmt.Sprintf("fx.Annotated{%v}", strings.Join(fields, ", "))
 }
 
-// annotations is used for building out the info needed to generate struct
-// with tags using reflection.
-type annotations struct {
-	fType     reflect.Type   // type of the function being annotated
-	asTargets []reflect.Type // list of interfaces the results of target function needs to be annotated as.
-	outTags   []string       // struct tags for the output, if any.
-
-	Ins  []reflect.Type // types of the annotated function's inputs, if any.
-	Outs []reflect.Type // types of the annotated function's outputs, if any.
-
-	annotatedIn  bool // whether the function's input is being annotated
-	annotatedOut bool // whether the function's output is being annotated
-	annotatedAs  bool // whether the function's output is being annotated as new types
-
-	returnsError  bool  // whether the function returns an error
-	resultOffsets []int // resultOffsets[N] gives the field offset of Nth result.
-}
-
-func newAnnotations(fType reflect.Type) annotations {
-	numIn := fType.NumIn()
-	numOut := fType.NumOut()
-	ins := make([]reflect.Type, numIn)
-	outs := make([]reflect.Type, numOut)
-
-	for i := 0; i < numIn; i++ {
-		ins[i] = fType.In(i)
-	}
-	for i := 0; i < numOut; i++ {
-		outs[i] = fType.Out(i)
-	}
-	return annotations{
-		fType: fType,
-		Ins:   ins,
-		Outs:  outs,
-	}
-}
-
 // field used for embedding fx.Out type in generated struct.
 var _outAnnotationField = reflect.StructField{
 	Name:      "Out",
@@ -137,60 +99,10 @@ var _outAnnotationField = reflect.StructField{
 	Anonymous: true,
 }
 
-// This generates the annotated Out struct when function is annotated
-// with fx.As and/or fx.ResultTags options.
-func (ann *annotations) genAnnotatedOutStruct() error {
-	if !ann.annotatedOut && !ann.annotatedAs {
-		return nil
-	}
-	fType := ann.fType
-	// offsets[i] is the index of this field in annotatedResult.
-	// This will always be >0 when valid.
-	offsets := make([]int, fType.NumOut())
-	annotatedResult := []reflect.StructField{_outAnnotationField}
-	for i := 0; i < fType.NumOut(); i++ {
-		if fType.Out(i) == _typeOfError {
-			ann.returnsError = true
-			continue
-		}
-		structFieldType, err := ann.structFieldType(i)
-		if err != nil {
-			return err
-		}
-		structField := reflect.StructField{
-			Name: fmt.Sprintf("Field%d", i),
-			Type: structFieldType,
-		}
-		if i < len(ann.outTags) {
-			structField.Tag = reflect.StructTag(ann.outTags[i])
-		}
-		offsets[i] = len(annotatedResult)
-		annotatedResult = append(annotatedResult, structField)
-	}
-	ann.Outs = []reflect.Type{reflect.StructOf(annotatedResult)}
-	ann.resultOffsets = offsets
-	if ann.returnsError {
-		ann.Outs = append(ann.Outs, _typeOfError)
-	}
-	return nil
-}
-
-// helper for getting type of a fx.Out struct field
-func (ann *annotations) structFieldType(i int) (reflect.Type, error) {
-	if !ann.annotatedAs {
-		return ann.fType.Out(i), nil
-	}
-	asType := ann.asTargets[i]
-	if !ann.fType.Out(i).Implements(asType) {
-		return nil, fmt.Errorf("invalid fx.As: %v does not implement %v", ann.fType, asType)
-	}
-	return asType, nil
-}
-
 // Annotation can be passed to Annotate(f interface{}, anns ...Annotation)
 // for annotating the parameter and result types of a function.
 type Annotation interface {
-	apply(*annotations) error
+	apply(*annotated) error
 }
 
 var (
@@ -232,30 +144,11 @@ var _ paramTagsAnnotation = paramTagsAnnotation{}
 // If there has already been a ParamTag that was applied, this
 // will return an error.
 
-func (pt paramTagsAnnotation) apply(ann *annotations) error {
-	if ann.annotatedIn {
+func (pt paramTagsAnnotation) apply(ann *annotated) error {
+	if len(ann.ParamTags) > 0 {
 		return errors.New("cannot apply more than one line of ParamTags")
 	}
-	ann.annotatedIn = true
-	fType := ann.fType
-
-	annotatedParams := []reflect.StructField{{
-		Name:      "In",
-		Type:      reflect.TypeOf(In{}),
-		Anonymous: true,
-	}}
-	for i := 0; i < fType.NumIn(); i++ {
-		structField := reflect.StructField{
-			Name: fmt.Sprintf("Field%d", i),
-			Type: fType.In(i),
-		}
-		if i < len(pt.tags) {
-			structField.Tag = reflect.StructTag(pt.tags[i])
-		}
-		annotatedParams = append(annotatedParams, structField)
-	}
-
-	ann.Ins = []reflect.Type{reflect.StructOf(annotatedParams)}
+	ann.ParamTags = pt.tags
 	return nil
 }
 
@@ -286,12 +179,11 @@ var _ resultTagsAnnotation = resultTagsAnnotation{}
 //
 // If there has already been a ResultTag that was applied, this
 // will return an error.
-func (rt resultTagsAnnotation) apply(ann *annotations) error {
-	if ann.annotatedOut {
+func (rt resultTagsAnnotation) apply(ann *annotated) error {
+	if len(ann.ResultTags) > 0 {
 		return errors.New("cannot apply more than one line of ResultTags")
 	}
-	ann.annotatedOut = true
-	ann.outTags = rt.tags
+	ann.ResultTags = rt.tags
 	return nil
 }
 
@@ -351,18 +243,236 @@ func As(interfaces ...interface{}) Annotation {
 	return asAnnotation{interfaces}
 }
 
-func (at asAnnotation) apply(ann *annotations) error {
-	if ann.annotatedAs {
+func (at asAnnotation) apply(ann *annotated) error {
+	if len(ann.As) > 0 {
 		return errors.New("cannot apply more than one line of As")
 	}
-	ann.annotatedAs = true
-	ann.asTargets = make([]reflect.Type, len(at.targets))
-	for i, target := range at.targets {
-		ann.asTargets[i] = reflect.TypeOf(target).Elem()
+	types := make([]reflect.Type, len(at.targets))
+	for i, typ := range at.targets {
+		t := reflect.TypeOf(typ)
+		if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Interface {
+			return fmt.Errorf("fx.As: argument must be a pointer to an interface: got %v", t)
+		}
+		t = t.Elem()
+		types[i] = t
 	}
+
+	ann.As = types
 	return nil
 }
 
+type annotated struct {
+	Target     interface{}
+	ParamTags  []string
+	ResultTags []string
+	As         []reflect.Type
+}
+
+func (ann annotated) String() string {
+	var sb strings.Builder
+	sb.WriteString("fx.Annotate(")
+	sb.WriteString(fxreflect.FuncName(ann.Target))
+	if tags := ann.ParamTags; len(tags) > 0 {
+		fmt.Fprintf(&sb, ", fx.ParamTags(%q)", tags)
+	}
+	if tags := ann.ResultTags; len(tags) > 0 {
+		fmt.Fprintf(&sb, ", fx.ResultTags(%q)", tags)
+	}
+	if as := ann.As; len(as) > 0 {
+		fmt.Fprintf(&sb, ", fx.As(%v)", as)
+	}
+	return sb.String()
+}
+
+// Build builds and returns a constructor based on fx.In/fx.Out params and
+// results wrapping the original constructor passed to fx.Annotate.
+func (ann *annotated) Build() (interface{}, error) {
+	if reflect.TypeOf(ann.Target).Kind() != reflect.Func {
+		return nil, fmt.Errorf("must provide constructor function, got %v (%T)", ann.Target, ann.Target)
+	}
+
+	paramTypes, remapParams := ann.parameters()
+	resultTypes, remapResults, err := ann.results()
+	if err != nil {
+		return nil, err
+	}
+
+	newFnType := reflect.FuncOf(paramTypes, resultTypes, false)
+	origFn := reflect.ValueOf(ann.Target)
+	newFn := reflect.MakeFunc(newFnType, func(args []reflect.Value) []reflect.Value {
+		args = remapParams(args)
+		results := origFn.Call(args)
+		results = remapResults(results)
+		return results
+	})
+
+	return newFn.Interface(), nil
+}
+
+// parameters returns the type for the parameters of the annotated function,
+// and a function that maps the arguments of the annotated function
+// back to the arguments of the target function.
+func (ann *annotated) parameters() (
+	types []reflect.Type,
+	remap func([]reflect.Value) []reflect.Value,
+) {
+	ft := reflect.TypeOf(ann.Target)
+
+	types = make([]reflect.Type, ft.NumIn())
+	for i := 0; i < ft.NumIn(); i++ {
+		types[i] = ft.In(i)
+	}
+
+	// No parameter annotations. Return the original types
+	// and an identity function.
+	if len(ann.ParamTags) == 0 {
+		return types, func(args []reflect.Value) []reflect.Value {
+			return args
+		}
+	}
+
+	// Turn parameters into an fx.In struct.
+	inFields := []reflect.StructField{
+		{
+			Name:      "In",
+			Type:      reflect.TypeOf(In{}),
+			Anonymous: true,
+		},
+	}
+
+	for i, t := range types {
+		field := reflect.StructField{
+			Name: fmt.Sprintf("Field%d", i),
+			Type: t,
+		}
+
+		if i < len(ann.ParamTags) {
+			field.Tag = reflect.StructTag(ann.ParamTags[i])
+		}
+
+		inFields = append(inFields, field)
+	}
+
+	types = []reflect.Type{reflect.StructOf(inFields)}
+	return types, func(args []reflect.Value) []reflect.Value {
+		params := args[0]
+		args = args[:0]
+		for i := 0; i < ft.NumIn(); i++ {
+			args = append(args, params.Field(i+1))
+		}
+		return args
+	}
+}
+
+// results returns the types of the results of the annotated function,
+// and a function that maps the results of the target function,
+// into a result compatible with the annotated function.
+func (ann *annotated) results() (
+	types []reflect.Type,
+	remap func([]reflect.Value) []reflect.Value,
+	err error,
+) {
+	ft := reflect.TypeOf(ann.Target)
+
+	types = make([]reflect.Type, ft.NumOut())
+	for i := 0; i < ft.NumOut(); i++ {
+		types[i] = ft.Out(i)
+	}
+
+	// No result annotations. Return the original types
+	// and an identity function.
+	if len(ann.ResultTags) == 0 && len(ann.As) == 0 {
+		return types, func(results []reflect.Value) []reflect.Value {
+			return results
+		}, nil
+	}
+
+	outFields := []reflect.StructField{
+		{
+			Name:      "Out",
+			Type:      reflect.TypeOf(Out{}),
+			Anonymous: true,
+		},
+	}
+
+	// offsets[i] is index of result i in the generated fx.Out
+	// struct.
+	offsets := make([]int, ft.NumOut())
+
+	var hasError bool
+	for i, t := range types {
+		if t == _typeOfError {
+			// Guarantee that:
+			// - only the last result is an error
+			// - there is at most one error result
+			if i != len(types)-1 {
+				return nil, nil, fmt.Errorf(
+					"only the last result can be an error: "+
+						"%v (%v) returns error as result %d",
+					fxreflect.FuncName(ann.Target), ft, i)
+			}
+			hasError = true
+			continue
+		}
+
+		field := reflect.StructField{
+			Name: fmt.Sprintf("Field%d", i),
+			Type: t,
+		}
+
+		if i < len(ann.As) {
+			if !t.Implements(ann.As[i]) {
+				return nil, nil, fmt.Errorf("invalid fx.As: %v does not implement %v", t, ann.As[i])
+			}
+			field.Type = ann.As[i]
+		}
+
+		if i < len(ann.ResultTags) {
+			field.Tag = reflect.StructTag(ann.ResultTags[i])
+		}
+
+		offsets[i] = len(outFields)
+		outFields = append(outFields, field)
+	}
+
+	outType := reflect.StructOf(outFields)
+	types = []reflect.Type{outType}
+	if hasError {
+		types = append(types, _typeOfError)
+	}
+
+	return types, func(results []reflect.Value) []reflect.Value {
+		out := reflect.New(outType).Elem()
+
+		var outErr error
+		for i, r := range results {
+			if i == len(results)-1 && hasError {
+				// If hasError and this is the last item,
+				// we are guaranteed that this is an error
+				// object.
+				if err, _ := r.Interface().(error); err != nil {
+					outErr = err
+				}
+				continue
+			}
+
+			out.Field(offsets[i]).Set(r)
+		}
+
+		results = results[:0]
+		results = append(results, out)
+		if hasError {
+			if outErr != nil {
+				results = append(results, reflect.ValueOf(outErr))
+			} else {
+				results = append(results, _nilError)
+			}
+		}
+
+		return results
+	}, nil
+}
+
 // Annotate lets you annotate a function's parameters and returns
 // without you having to declare separate struct definitions for them.
 //
@@ -412,84 +522,15 @@ func (at asAnnotation) apply(ann *annotations) error {
 //
 // If more tags are given than the number of parameters/results, only
 // the ones up to the number of parameters/results will be applied.
-func Annotate(f interface{}, anns ...Annotation) interface{} {
-	fVal := reflect.ValueOf(f)
-	fType := fVal.Type()
-	numIn := fType.NumIn()
-	numOut := fType.NumOut()
-	annotations := newAnnotations(fType)
-
+func Annotate(t interface{}, anns ...Annotation) interface{} {
+	result := annotated{Target: t}
 	for _, ann := range anns {
-		if e := ann.apply(&annotations); e != nil {
+		if err := ann.apply(&result); err != nil {
 			return annotationError{
-				target: f,
-				err:    e,
-			}
-		}
-	}
-
-	if err := annotations.genAnnotatedOutStruct(); err != nil {
-		return annotationError{
-			target: f,
-			err:    err,
-		}
-	}
-
-	ins := annotations.Ins
-	outs := annotations.Outs
-	resultOffsets := annotations.resultOffsets
-
-	newF := func(args []reflect.Value) []reflect.Value {
-		var fParams, fResults []reflect.Value
-		if annotations.annotatedIn {
-			fParams = make([]reflect.Value, numIn)
-			params := args[0]
-			for i := 0; i < numIn; i++ {
-				fParams[i] = params.Field(i + 1)
+				target: t,
+				err:    err,
 			}
-		} else {
-			fParams = args
-		}
-
-		// Call the wrapped function.
-		fResults = fVal.Call(fParams)
-
-		// If the function's output wasn't annotated and we don't need
-		// to provide it as another type, there's no need to generate
-		// an fx.Out embedded struct so we return early.
-		if !annotations.annotatedOut && !annotations.annotatedAs {
-			return fResults
 		}
-
-		var errValue reflect.Value
-
-		// wrap the result in an annotated struct
-		results := reflect.New(outs[0]).Elem()
-
-		// aggregate the errors from the annotated function
-		// into one error.
-		var errResults error
-		for i := 0; i < numOut; i++ {
-			if fResults[i].Type() == _typeOfError {
-				if err, _ := fResults[i].Interface().(error); err != nil {
-					errResults = multierr.Append(errResults, err)
-				}
-				continue
-			}
-			results.Field(resultOffsets[i]).Set(fResults[i])
-		}
-		if annotations.returnsError {
-			if errResults != nil {
-				errValue = reflect.ValueOf(errResults)
-				return []reflect.Value{results, errValue}
-			}
-			// error is nil. Return nil error Value.
-			return []reflect.Value{results, _nilError}
-		}
-		return []reflect.Value{results}
 	}
-
-	annotatedFuncType := reflect.FuncOf(ins, outs, false)
-	annotatedFunc := reflect.MakeFunc(annotatedFuncType, newF)
-	return annotatedFunc.Interface()
+	return result
 }
diff --git a/app.go b/app.go
index ad5a892..2909c5f 100644
--- a/app.go
+++ b/app.go
@@ -858,13 +858,27 @@ func (app *App) provide(p provide) {
 		app.log.LogEvent(ev)
 	}()
 
-	if annError, ok := constructor.(annotationError); ok {
-		app.err = fmt.Errorf("encountered error while applying annotation using fx.Annotate to %s: %+v",
-			fxreflect.FuncName(annError.target), annError.err)
+	switch constructor := constructor.(type) {
+	case annotationError:
+		// fx.Annotate failed. Turn it into an Fx error.
+		app.err = fmt.Errorf(
+			"encountered error while applying annotation using fx.Annotate to %s: %+v",
+			fxreflect.FuncName(constructor.target), constructor.err)
 		return
-	}
 
-	if ann, ok := constructor.(Annotated); ok {
+	case annotated:
+		c, err := constructor.Build()
+		if err != nil {
+			app.err = fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", constructor, p.Stack, err)
+			return
+		}
+
+		if err := app.container.Provide(c, opts...); err != nil {
+			app.err = fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", constructor, p.Stack, err)
+		}
+
+	case Annotated:
+		ann := constructor
 		switch {
 		case len(ann.Group) > 0 && len(ann.Name) > 0:
 			app.err = fmt.Errorf(
@@ -880,29 +894,30 @@ func (app *App) provide(p provide) {
 		if err := app.container.Provide(ann.Target, opts...); err != nil {
 			app.err = fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", ann, p.Stack, err)
 		}
-		return
-	}
-
-	if reflect.TypeOf(constructor).Kind() == reflect.Func {
-		ft := reflect.ValueOf(constructor).Type()
 
-		for i := 0; i < ft.NumOut(); i++ {
-			t := ft.Out(i)
-
-			if t == reflect.TypeOf(Annotated{}) {
-				app.err = fmt.Errorf(
-					"fx.Annotated should be passed to fx.Provide directly, "+
-						"it should not be returned by the constructor: "+
-						"fx.Provide received %v from:\n%+v",
-					fxreflect.FuncName(constructor), p.Stack)
-				return
+	default:
+		if reflect.TypeOf(constructor).Kind() == reflect.Func {
+			ft := reflect.ValueOf(constructor).Type()
+
+			for i := 0; i < ft.NumOut(); i++ {
+				t := ft.Out(i)
+
+				if t == reflect.TypeOf(Annotated{}) {
+					app.err = fmt.Errorf(
+						"fx.Annotated should be passed to fx.Provide directly, "+
+							"it should not be returned by the constructor: "+
+							"fx.Provide received %v from:\n%+v",
+						fxreflect.FuncName(constructor), p.Stack)
+					return
+				}
 			}
 		}
-	}
 
-	if err := app.container.Provide(constructor, opts...); err != nil {
-		app.err = fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", fxreflect.FuncName(constructor), p.Stack, err)
+		if err := app.container.Provide(constructor, opts...); err != nil {
+			app.err = fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", fxreflect.FuncName(constructor), p.Stack, err)
+		}
 	}
+
 }
 
 // Execute invokes in order supplied to New, returning the first error
@@ -932,13 +947,22 @@ func (app *App) executeInvoke(i invoke) (err error) {
 		})
 	}()
 
-	if _, ok := fn.(Option); ok {
+	switch fn := fn.(type) {
+	case Option:
 		return fmt.Errorf("fx.Option should be passed to fx.New directly, "+
 			"not to fx.Invoke: fx.Invoke received %v from:\n%+v",
 			fn, i.Stack)
-	}
 
-	return app.container.Invoke(fn)
+	case annotated:
+		c, err := fn.Build()
+		if err != nil {
+			return err
+		}
+
+		return app.container.Invoke(c)
+	default:
+		return app.container.Invoke(fn)
+	}
 }
 
 type withTimeoutParams struct {
diff --git a/supply.go b/supply.go
index 4b68591..232a493 100644
--- a/supply.go
+++ b/supply.go
@@ -58,6 +58,11 @@ func Supply(values ...interface{}) Option {
 	types := make([]reflect.Type, len(values))
 	for i, value := range values {
 		switch value := value.(type) {
+		case annotated:
+			var typ reflect.Type
+			value.Target, typ = newSupplyConstructor(value.Target)
+			constructors[i] = value
+			types[i] = typ
 		case Annotated:
 			var typ reflect.Type
 			value.Target, typ = newSupplyConstructor(value.Target)


Test patch:
diff --git a/annotated_test.go b/annotated_test.go
index ca72445..be7362e 100644
--- a/annotated_test.go
+++ b/annotated_test.go
@@ -194,6 +194,15 @@ func TestAnnotatedAs(t *testing.T) {
 				assert.Equal(t, "a good stringer", ms.String())
 			},
 		},
+		{
+			desc: "annotate fx.Supply",
+			provide: fx.Supply(
+				fx.Annotate(&asStringer{"foo"}, fx.As(new(fmt.Stringer))),
+			),
+			invoke: func(s fmt.Stringer) {
+				assert.Equal(t, "foo", s.String())
+			},
+		},
 	}
 
 	for _, tt := range tests {
@@ -216,6 +225,10 @@ func TestAnnotatedAs(t *testing.T) {
 func TestAnnotatedAsFailures(t *testing.T) {
 	t.Parallel()
 
+	newAsStringer := func() *asStringer {
+		return &asStringer{name: "stringer"}
+	}
+
 	tests := []struct {
 		desc          string
 		provide       fx.Option
@@ -223,26 +236,20 @@ func TestAnnotatedAsFailures(t *testing.T) {
 		errorContains string
 	}{
 		{
-			desc: "provide when an illegal type As",
-			provide: fx.Provide(fx.Annotate(func() *asStringer {
-				return &asStringer{name: "stringer"}
-			}, fx.As(new(io.Writer)))),
+			desc:          "provide when an illegal type As",
+			provide:       fx.Provide(fx.Annotate(newAsStringer, fx.As(new(io.Writer)))),
 			invoke:        func() {},
 			errorContains: "does not implement",
 		},
 		{
-			desc: "provide two lines of As",
-			provide: fx.Provide(fx.Annotate(func() *asStringer {
-				return &asStringer{name: "stringer"}
-			}, fx.As(new(io.Writer)), fx.As(new(io.Reader)))),
+			desc:          "provide two lines of As",
+			provide:       fx.Provide(fx.Annotate(newAsStringer, fx.As(new(io.Writer)), fx.As(new(io.Reader)))),
 			invoke:        func() {},
 			errorContains: "cannot apply more than one line of As",
 		},
 		{
-			desc: "don't provide original type using As",
-			provide: fx.Provide(fx.Annotate(func() *asStringer {
-				return &asStringer{name: "stringer"}
-			}, fx.As(new(fmt.Stringer)))),
+			desc:    "don't provide original type using As",
+			provide: fx.Provide(fx.Annotate(newAsStringer, fx.As(new(fmt.Stringer)))),
 			invoke: func(as *asStringer) {
 				fmt.Println(as.String())
 			},
@@ -258,6 +265,16 @@ func TestAnnotatedAsFailures(t *testing.T) {
 			},
 			errorContains: `missing type: string[name="n"]`,
 		},
+		{
+			desc: "non-pointer argument to As",
+			provide: fx.Provide(
+				fx.Annotate(
+					newAsStringer,
+					fx.As("foo"),
+				),
+			),
+			errorContains: "argument must be a pointer to an interface: got string",
+		},
 	}
 
 	for _, tt := range tests {
@@ -535,15 +552,34 @@ func TestAnnotate(t *testing.T) {
 		assert.Contains(t, err.Error(), `missing type: *fx_test.a[name="thirdA"]`)
 	})
 
-	t.Run("provide with annotated results with error", func(t *testing.T) {
+	t.Run("error in the middle of a function", func(t *testing.T) {
 		t.Parallel()
 
-		app := fxtest.New(t,
+		app := NewForTest(t,
 			fx.Provide(
 				//lint:ignore ST1008 we want to test error in the middle.
 				fx.Annotate(func() (*a, error, *a) {
 					return &a{}, nil, &a{}
 				}, fx.ResultTags(`name:"firstA"`, ``, `name:"secondA"`)),
+			),
+			fx.Invoke(
+				fx.Annotate(func(*a) {}, fx.ParamTags(`name:"firstA"`)),
+			),
+		)
+		err := app.Err()
+		require.Error(t, err)
+		assert.Contains(t, err.Error(), "only the last result can be an error")
+		assert.Contains(t, err.Error(), "returns error as result 1")
+	})
+
+	t.Run("provide with annotated results with error", func(t *testing.T) {
+		t.Parallel()
+
+		app := fxtest.New(t,
+			fx.Provide(
+				fx.Annotate(func() (*a, *a, error) {
+					return &a{}, &a{}, nil
+				}, fx.ResultTags(`name:"firstA"`, `name:"secondA"`)),
 				fx.Annotate(func() (*a, error) {
 					return &a{}, nil
 				}, fx.ResultTags(`name:"thirdA"`)),
@@ -653,4 +689,40 @@ func TestAnnotate(t *testing.T) {
 		require.Error(t, err)
 		assert.Contains(t, err.Error(), "some error")
 	})
+
+	t.Run("provide annotated non-function", func(t *testing.T) {
+		t.Parallel()
+
+		app := NewForTest(t,
+			fx.Provide(
+				fx.Annotate(42, fx.ResultTags(`name:"buf"`)),
+			),
+		)
+		err := app.Err()
+		require.Error(t, err)
+
+		// Exmaple:
+		// fx.Provide(fx.Annotate(42, fx.ResultTags(["name:\"buf\""])) from:
+		// go.uber.org/fx_test.TestAnnotate.func17
+		//     /Users/abg/dev/fx/annotated_test.go:697
+		// testing.tRunner
+		//     /usr/local/Cellar/go/1.17.2/libexec/src/testing/testing.go:1259
+		// Failed: must provide constructor function, got 42 (int)
+
+		assert.Contains(t, err.Error(), "fx.Provide(fx.Annotate(42")
+		assert.Contains(t, err.Error(), "must provide constructor function, got 42 (int)")
+	})
+
+	t.Run("invoke annotated non-function", func(t *testing.T) {
+		t.Parallel()
+
+		app := NewForTest(t,
+			fx.Invoke(
+				fx.Annotate(42, fx.ParamTags(`name:"buf"`)),
+			),
+		)
+		err := app.Err()
+		require.Error(t, err)
+		assert.Contains(t, err.Error(), "must provide constructor function, got 42 (int)")
+	})
 }
diff --git a/app_test.go b/app_test.go
index 00280f2..8b08633 100644
--- a/app_test.go
+++ b/app_test.go
@@ -295,6 +295,35 @@ func TestNewApp(t *testing.T) {
 		assert.Contains(t, err.Error(), "Failed: must provide constructor function")
 	})
 
+	t.Run("ErrorProvidingAnnotate", func(t *testing.T) {
+		t.Parallel()
+
+		type t1 struct{}
+		newT1 := func() t1 { return t1{} }
+
+		// Provide twice.
+		app := NewForTest(t, Provide(
+			Annotate(newT1, ResultTags(`name:"foo"`)),
+			Annotate(newT1, ResultTags(`name:"foo"`)),
+		))
+
+		err := app.Err()
+		require.Error(t, err)
+
+		// Example:
+		// fx.Provide(fx.Annotate(go.uber.org/fx_test.TestNewApp.func10.1(), fx.ResultTags(["name:\"foo\""])) from:
+		//     go.uber.org/fx_test.TestNewApp.func10
+		//         /.../fx/app_test.go:305
+		//     testing.tRunner
+		//         /.../src/testing/testing.go:1259
+		//     Failed: cannot provide function "reflect".makeFuncStub (/.../reflect/asm_amd64.s:30):
+		//     cannot provide fx_test.t1[name="foo"] from [0].Field0:
+		//     already provided by "reflect".makeFuncStub (/.../reflect/asm_amd64.s:30)
+		assert.Contains(t, err.Error(), `fx.Provide(fx.Annotate(`)
+		assert.Contains(t, err.Error(), `fx.ResultTags(["name:\"foo\""])`)
+		assert.Contains(t, err.Error(), "already provided")
+	})
+
 	t.Run("ErrorProviding", func(t *testing.T) {
 		t.Parallel()