| 729 | } |
| 730 | |
| 731 | Status GraphConstructor::ValidateShape(Node* node) { |
| 732 | if (!opts_.importing || !opts_.validate_shape) return Status::OK(); |
| 733 | TF_RETURN_IF_ERROR(refiner_->AddNode(node)); |
| 734 | // For nodes with the _output_shapes attribute, override the shape. |
| 735 | std::vector<const TensorShapeProto*> shape_attrs; |
| 736 | const char* kAttrName = "_output_shapes"; |
| 737 | if (!TryGetNodeAttr(node->attrs(), kAttrName, &shape_attrs)) { |
| 738 | // No _output_shapes attribute, the AddNode call above was sufficient. |
| 739 | return Status::OK(); |
| 740 | } |
| 741 | auto* ic = refiner_->GetContext(node); |
| 742 | DCHECK(ic != nullptr) |
| 743 | << "ShapeRefiner::AddNode() should have created the InferenceContext"; |
| 744 | if (shape_attrs.size() < node->num_outputs()) { |
| 745 | return errors::InvalidArgument( |
| 746 | "Node '", node->name(), "' has ", node->num_outputs(), |
| 747 | " outputs but the ", kAttrName, " attribute specifies shapes for ", |
| 748 | shape_attrs.size(), " outputs"); |
| 749 | } |
| 750 | // NOTE(skyewm): we don't raise an error here because some users depend on |
| 751 | // this behavior, even though it's unsafe. |
| 752 | // TODO(b/74619486): raise an error. |
| 753 | if (shape_attrs.size() > node->num_outputs()) { |
| 754 | LOG(WARNING) << "Node '" << node->name() << "' has " << node->num_outputs() |
| 755 | << " outputs but the " << kAttrName |
| 756 | << " attribute specifies shapes for " << shape_attrs.size() |
| 757 | << " outputs. Output shapes may be inaccurate."; |
| 758 | } |
| 759 | for (int i = 0; i < node->num_outputs(); ++i) { |
| 760 | const TensorShapeProto& p = *shape_attrs[i]; |
| 761 | shape_inference::ShapeHandle h; |
| 762 | Status s = ic->MakeShapeFromShapeProto(p, &h); |
| 763 | if (!s.ok()) { |
| 764 | return errors::InvalidArgument("Node '", node->name(), " has an invalid ", |
| 765 | kAttrName, " attribute (shape #", i, |
| 766 | " error:'", s.error_message(), "'"); |
| 767 | } |
| 768 | s = refiner_->SetShape(node, i, h); |
| 769 | if (!s.ok()) { |
| 770 | // If the output shape is incompatible with what is inferred |
| 771 | // by the graph for a very specific whitelist of ops, then we |
| 772 | // ignore this output shape. This can happen if there is a |
| 773 | // bug in the shape function for some operation, and the |
| 774 | // serialized graph def has the incorrect shape set when |
| 775 | // running on a newer binary with the fixed shape function. |
| 776 | // This is an escape hatch that allows us to correct shape |
| 777 | // functions that are not critical to correct execution but |
| 778 | // would cause graphs to fail if imported after correcting. |
| 779 | const string& op = node->type_string(); |
| 780 | const std::vector<string> whitelist = { |
| 781 | // To be removed after 2017/03/08. |
| 782 | "RandomShuffleQueue", |
| 783 | "PaddingFIFOQueue", |
| 784 | "FIFOQueue", |
| 785 | "PriorityQueue", |
| 786 | "QueueSize", |
| 787 | "Stack", |
| 788 | "Barrier", |
no test coverage detected